Centralized logging
This commit is contained in:
parent
02e7c2946b
commit
5658801f72
10 changed files with 336 additions and 373 deletions
|
@ -6,8 +6,8 @@ import json
|
||||||
import requests
|
import requests
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import utils
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
|
from utils import Logging, window
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -34,28 +34,26 @@ class ConnectUtils():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def setUserId(self, userId):
|
def setUserId(self, userId):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.userId = userId
|
self.userId = userId
|
||||||
self.logMsg("Set connect userId: %s" % userId, 2)
|
log("Set connect userId: %s" % userId, 2)
|
||||||
|
|
||||||
def setServer(self, server):
|
def setServer(self, server):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.server = server
|
self.server = server
|
||||||
self.logMsg("Set connect server: %s" % server, 2)
|
log("Set connect server: %s" % server, 2)
|
||||||
|
|
||||||
def setToken(self, token):
|
def setToken(self, token):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.token = token
|
self.token = token
|
||||||
self.logMsg("Set connect token: %s" % token, 2)
|
log("Set connect token: %s" % token, 2)
|
||||||
|
|
||||||
|
|
||||||
def startSession(self):
|
def startSession(self):
|
||||||
|
@ -73,7 +71,7 @@ class ConnectUtils():
|
||||||
if self.sslclient is not None:
|
if self.sslclient is not None:
|
||||||
verify = self.sslclient
|
verify = self.sslclient
|
||||||
except:
|
except:
|
||||||
self.logMsg("Could not load SSL settings.", 1)
|
log("Could not load SSL settings.", 1)
|
||||||
|
|
||||||
# Start session
|
# Start session
|
||||||
self.c = requests.Session()
|
self.c = requests.Session()
|
||||||
|
@ -83,13 +81,13 @@ class ConnectUtils():
|
||||||
self.c.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
self.c.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||||
self.c.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
self.c.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||||
|
|
||||||
self.logMsg("Requests session started on: %s" % self.server, 1)
|
log("Requests session started on: %s" % self.server, 1)
|
||||||
|
|
||||||
def stopSession(self):
|
def stopSession(self):
|
||||||
try:
|
try:
|
||||||
self.c.close()
|
self.c.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logMsg("Requests session could not be terminated: %s" % e, 1)
|
log("Requests session could not be terminated: %s" % e, 1)
|
||||||
|
|
||||||
def getHeader(self, authenticate=True):
|
def getHeader(self, authenticate=True):
|
||||||
|
|
||||||
|
@ -103,7 +101,7 @@ class ConnectUtils():
|
||||||
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
'Accept': "application/json"
|
'Accept': "application/json"
|
||||||
}
|
}
|
||||||
self.logMsg("Header: %s" % header, 1)
|
log("Header: %s" % header, 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
token = self.token
|
token = self.token
|
||||||
|
@ -115,17 +113,17 @@ class ConnectUtils():
|
||||||
'X-Application': "Kodi/%s" % version,
|
'X-Application': "Kodi/%s" % version,
|
||||||
'X-Connect-UserToken': token
|
'X-Connect-UserToken': token
|
||||||
}
|
}
|
||||||
self.logMsg("Header: %s" % header, 1)
|
log("Header: %s" % header, 1)
|
||||||
|
|
||||||
return header
|
return header
|
||||||
|
|
||||||
def doUrl(self, url, data=None, postBody=None, rtype="GET",
|
def doUrl(self, url, data=None, postBody=None, rtype="GET",
|
||||||
parameters=None, authenticate=True, timeout=None):
|
parameters=None, authenticate=True, timeout=None):
|
||||||
|
|
||||||
window = utils.window
|
log("=== ENTER connectUrl ===", 2)
|
||||||
|
|
||||||
self.logMsg("=== ENTER connectUrl ===", 2)
|
|
||||||
default_link = ""
|
default_link = ""
|
||||||
|
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
timeout = self.timeout
|
timeout = self.timeout
|
||||||
|
|
||||||
|
@ -209,25 +207,25 @@ class ConnectUtils():
|
||||||
verify=verifyssl)
|
verify=verifyssl)
|
||||||
|
|
||||||
##### THE RESPONSE #####
|
##### THE RESPONSE #####
|
||||||
self.logMsg(r.url, 1)
|
log(r.url, 1)
|
||||||
self.logMsg(r, 1)
|
log(r, 1)
|
||||||
|
|
||||||
if r.status_code == 204:
|
if r.status_code == 204:
|
||||||
# No body in the response
|
# No body in the response
|
||||||
self.logMsg("====== 204 Success ======", 1)
|
log("====== 204 Success ======", 1)
|
||||||
|
|
||||||
elif r.status_code == requests.codes.ok:
|
elif r.status_code == requests.codes.ok:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# UNICODE - JSON object
|
# UNICODE - JSON object
|
||||||
r = r.json()
|
r = r.json()
|
||||||
self.logMsg("====== 200 Success ======", 1)
|
log("====== 200 Success ======", 1)
|
||||||
self.logMsg("Response: %s" % r, 1)
|
log("Response: %s" % r, 1)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
except:
|
except:
|
||||||
if r.headers.get('content-type') != "text/html":
|
if r.headers.get('content-type') != "text/html":
|
||||||
self.logMsg("Unable to convert the response for: %s" % url, 1)
|
log("Unable to convert the response for: %s" % url, 1)
|
||||||
else:
|
else:
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
|
||||||
|
@ -238,8 +236,8 @@ class ConnectUtils():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
except requests.exceptions.ConnectTimeout as e:
|
except requests.exceptions.ConnectTimeout as e:
|
||||||
self.logMsg("Server timeout at: %s" % url, 0)
|
log("Server timeout at: %s" % url, 0)
|
||||||
self.logMsg(e, 1)
|
log(e, 1)
|
||||||
|
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
|
|
||||||
|
@ -255,11 +253,11 @@ class ConnectUtils():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
except requests.exceptions.SSLError as e:
|
except requests.exceptions.SSLError as e:
|
||||||
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
|
log("Invalid SSL certificate for: %s" % url, 0)
|
||||||
self.logMsg(e, 1)
|
log(e, 1)
|
||||||
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
self.logMsg("Unknown error connecting to: %s" % url, 0)
|
log("Unknown error connecting to: %s" % url, 0)
|
||||||
self.logMsg(e, 1)
|
log(e, 1)
|
||||||
|
|
||||||
return default_link
|
return default_link
|
|
@ -9,14 +9,15 @@ import logging
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
import utils
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
|
from utils import Logging, window, settings
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
# Disable requests logging
|
# Disable requests logging
|
||||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning
|
||||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
|
requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
|
||||||
#logging.getLogger('requests').setLevel(logging.WARNING)
|
#logging.getLogger('requests').setLevel(logging.WARNING)
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
@ -36,40 +37,38 @@ class DownloadUtils():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def setUsername(self, username):
|
def setUsername(self, username):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.username = username
|
self.username = username
|
||||||
self.logMsg("Set username: %s" % username, 2)
|
log("Set username: %s" % username, 2)
|
||||||
|
|
||||||
def setUserId(self, userId):
|
def setUserId(self, userId):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.userId = userId
|
self.userId = userId
|
||||||
self.logMsg("Set userId: %s" % userId, 2)
|
log("Set userId: %s" % userId, 2)
|
||||||
|
|
||||||
def setServer(self, server):
|
def setServer(self, server):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.server = server
|
self.server = server
|
||||||
self.logMsg("Set server: %s" % server, 2)
|
log("Set server: %s" % server, 2)
|
||||||
|
|
||||||
def setToken(self, token):
|
def setToken(self, token):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.token = token
|
self.token = token
|
||||||
self.logMsg("Set token: %s" % token, 2)
|
log("Set token: %s" % token, 2)
|
||||||
|
|
||||||
def setSSL(self, ssl, sslclient):
|
def setSSL(self, ssl, sslclient):
|
||||||
# Reserved for userclient only
|
# Reserved for userclient only
|
||||||
self.sslverify = ssl
|
self.sslverify = ssl
|
||||||
self.sslclient = sslclient
|
self.sslclient = sslclient
|
||||||
self.logMsg("Verify SSL host certificate: %s" % ssl, 2)
|
log("Verify SSL host certificate: %s" % ssl, 2)
|
||||||
self.logMsg("SSL client side certificate: %s" % sslclient, 2)
|
log("SSL client side certificate: %s" % sslclient, 2)
|
||||||
|
|
||||||
|
|
||||||
def postCapabilities(self, deviceId):
|
def postCapabilities(self, deviceId):
|
||||||
|
@ -94,11 +93,11 @@ class DownloadUtils():
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.logMsg("Capabilities URL: %s" % url, 2)
|
log("Capabilities URL: %s" % url, 2)
|
||||||
self.logMsg("Postdata: %s" % data, 2)
|
log("Postdata: %s" % data, 2)
|
||||||
|
|
||||||
self.downloadUrl(url, postBody=data, action_type="POST")
|
self.downloadUrl(url, postBody=data, action_type="POST")
|
||||||
self.logMsg("Posted capabilities to %s" % self.server, 2)
|
log("Posted capabilities to %s" % self.server, 2)
|
||||||
|
|
||||||
# Attempt at getting sessionId
|
# Attempt at getting sessionId
|
||||||
url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
|
url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
|
||||||
|
@ -107,20 +106,19 @@ class DownloadUtils():
|
||||||
sessionId = result[0]['Id']
|
sessionId = result[0]['Id']
|
||||||
|
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Failed to retrieve sessionId.", 1)
|
log("Failed to retrieve sessionId.", 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg("Session: %s" % result, 2)
|
log("Session: %s" % result, 2)
|
||||||
self.logMsg("SessionId: %s" % sessionId, 1)
|
log("SessionId: %s" % sessionId, 1)
|
||||||
utils.window('emby_sessionId', value=sessionId)
|
window('emby_sessionId', value=sessionId)
|
||||||
|
|
||||||
# Post any permanent additional users
|
# Post any permanent additional users
|
||||||
additionalUsers = utils.settings('additionalUsers')
|
additionalUsers = settings('additionalUsers')
|
||||||
if additionalUsers:
|
if additionalUsers:
|
||||||
|
|
||||||
additionalUsers = additionalUsers.split(',')
|
additionalUsers = additionalUsers.split(',')
|
||||||
self.logMsg(
|
log("List of permanent users added to the session: %s"
|
||||||
"List of permanent users added to the session: %s"
|
|
||||||
% additionalUsers, 1)
|
% additionalUsers, 1)
|
||||||
|
|
||||||
# Get the user list from server to get the userId
|
# Get the user list from server to get the userId
|
||||||
|
@ -158,7 +156,7 @@ class DownloadUtils():
|
||||||
if self.sslclient is not None:
|
if self.sslclient is not None:
|
||||||
verify = self.sslclient
|
verify = self.sslclient
|
||||||
except:
|
except:
|
||||||
self.logMsg("Could not load SSL settings.", 1)
|
log("Could not load SSL settings.", 1)
|
||||||
|
|
||||||
# Start session
|
# Start session
|
||||||
self.s = requests.Session()
|
self.s = requests.Session()
|
||||||
|
@ -168,18 +166,18 @@ class DownloadUtils():
|
||||||
self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||||
self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||||
|
|
||||||
self.logMsg("Requests session started on: %s" % self.server, 1)
|
log("Requests session started on: %s" % self.server, 1)
|
||||||
|
|
||||||
def stopSession(self):
|
def stopSession(self):
|
||||||
try:
|
try:
|
||||||
self.s.close()
|
self.s.close()
|
||||||
except:
|
except:
|
||||||
self.logMsg("Requests session could not be terminated.", 1)
|
log("Requests session could not be terminated.", 1)
|
||||||
|
|
||||||
def getHeader(self, authenticate=True):
|
def getHeader(self, authenticate=True):
|
||||||
|
|
||||||
deviceName = self.clientInfo.getDeviceName()
|
deviceName = self.clientInfo.getDeviceName()
|
||||||
deviceName = utils.normalize_string(deviceName.encode('utf-8'))
|
deviceName = deviceName.encode('utf-8')
|
||||||
deviceId = self.clientInfo.getDeviceId()
|
deviceId = self.clientInfo.getDeviceId()
|
||||||
version = self.clientInfo.getVersion()
|
version = self.clientInfo.getVersion()
|
||||||
|
|
||||||
|
@ -195,7 +193,7 @@ class DownloadUtils():
|
||||||
'Accept-Charset': 'UTF-8,*',
|
'Accept-Charset': 'UTF-8,*',
|
||||||
'Authorization': auth
|
'Authorization': auth
|
||||||
}
|
}
|
||||||
self.logMsg("Header: %s" % header, 2)
|
log("Header: %s" % header, 2)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
userId = self.userId
|
userId = self.userId
|
||||||
|
@ -212,19 +210,20 @@ class DownloadUtils():
|
||||||
'Authorization': auth,
|
'Authorization': auth,
|
||||||
'X-MediaBrowser-Token': token
|
'X-MediaBrowser-Token': token
|
||||||
}
|
}
|
||||||
self.logMsg("Header: %s" % header, 2)
|
log("Header: %s" % header, 2)
|
||||||
|
|
||||||
return header
|
return header
|
||||||
|
|
||||||
def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None, authenticate=True):
|
def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None,
|
||||||
|
authenticate=True):
|
||||||
|
|
||||||
self.logMsg("=== ENTER downloadUrl ===", 2)
|
log("=== ENTER downloadUrl ===", 2)
|
||||||
|
|
||||||
default_link = ""
|
default_link = ""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# If user is authenticated
|
# If user is authenticated
|
||||||
if (authenticate):
|
if authenticate:
|
||||||
# Get requests session
|
# Get requests session
|
||||||
try:
|
try:
|
||||||
s = self.s
|
s = self.s
|
||||||
|
@ -243,18 +242,18 @@ class DownloadUtils():
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# request session does not exists
|
# request session does not exists
|
||||||
# Get user information
|
# Get user information
|
||||||
self.userId = utils.window('emby_currUser')
|
self.userId = window('emby_currUser')
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = window('emby_server%s' % self.userId)
|
||||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
self.token = window('emby_accessToken%s' % self.userId)
|
||||||
header = self.getHeader()
|
header = self.getHeader()
|
||||||
verifyssl = False
|
verifyssl = False
|
||||||
cert = None
|
cert = None
|
||||||
|
|
||||||
# IF user enables ssl verification
|
# IF user enables ssl verification
|
||||||
if utils.settings('sslverify') == "true":
|
if settings('sslverify') == "true":
|
||||||
verifyssl = True
|
verifyssl = True
|
||||||
if utils.settings('sslcert') != "None":
|
if settings('sslcert') != "None":
|
||||||
verifyssl = utils.settings('sslcert')
|
verifyssl = settings('sslcert')
|
||||||
|
|
||||||
# Replace for the real values
|
# Replace for the real values
|
||||||
url = url.replace("{server}", self.server)
|
url = url.replace("{server}", self.server)
|
||||||
|
@ -314,23 +313,23 @@ class DownloadUtils():
|
||||||
verify=verifyssl)
|
verify=verifyssl)
|
||||||
|
|
||||||
##### THE RESPONSE #####
|
##### THE RESPONSE #####
|
||||||
self.logMsg(r.url, 2)
|
log(r.url, 2)
|
||||||
if r.status_code == 204:
|
if r.status_code == 204:
|
||||||
# No body in the response
|
# No body in the response
|
||||||
self.logMsg("====== 204 Success ======", 2)
|
log("====== 204 Success ======", 2)
|
||||||
|
|
||||||
elif r.status_code == requests.codes.ok:
|
elif r.status_code == requests.codes.ok:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# UNICODE - JSON object
|
# UNICODE - JSON object
|
||||||
r = r.json()
|
r = r.json()
|
||||||
self.logMsg("====== 200 Success ======", 2)
|
log("====== 200 Success ======", 2)
|
||||||
self.logMsg("Response: %s" % r, 2)
|
log("Response: %s" % r, 2)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
except:
|
except:
|
||||||
if r.headers.get('content-type') != "text/html":
|
if r.headers.get('content-type') != "text/html":
|
||||||
self.logMsg("Unable to convert the response for: %s" % url, 1)
|
log("Unable to convert the response for: %s" % url, 1)
|
||||||
else:
|
else:
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
|
||||||
|
@ -338,26 +337,26 @@ class DownloadUtils():
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError as e:
|
except requests.exceptions.ConnectionError as e:
|
||||||
# Make the addon aware of status
|
# Make the addon aware of status
|
||||||
if utils.window('emby_online') != "false":
|
if window('emby_online') != "false":
|
||||||
self.logMsg("Server unreachable at: %s" % url, 0)
|
log("Server unreachable at: %s" % url, 0)
|
||||||
self.logMsg(e, 2)
|
log(e, 2)
|
||||||
utils.window('emby_online', value="false")
|
window('emby_online', value="false")
|
||||||
|
|
||||||
except requests.exceptions.ConnectTimeout as e:
|
except requests.exceptions.ConnectTimeout as e:
|
||||||
self.logMsg("Server timeout at: %s" % url, 0)
|
log("Server timeout at: %s" % url, 0)
|
||||||
self.logMsg(e, 1)
|
log(e, 1)
|
||||||
|
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
|
|
||||||
if r.status_code == 401:
|
if r.status_code == 401:
|
||||||
# Unauthorized
|
# Unauthorized
|
||||||
status = utils.window('emby_serverStatus')
|
status = window('emby_serverStatus')
|
||||||
|
|
||||||
if 'X-Application-Error-Code' in r.headers:
|
if 'X-Application-Error-Code' in r.headers:
|
||||||
# Emby server errors
|
# Emby server errors
|
||||||
if r.headers['X-Application-Error-Code'] == "ParentalControl":
|
if r.headers['X-Application-Error-Code'] == "ParentalControl":
|
||||||
# Parental control - access restricted
|
# Parental control - access restricted
|
||||||
utils.window('emby_serverStatus', value="restricted")
|
window('emby_serverStatus', value="restricted")
|
||||||
xbmcgui.Dialog().notification(
|
xbmcgui.Dialog().notification(
|
||||||
heading="Emby server",
|
heading="Emby server",
|
||||||
message="Access restricted.",
|
message="Access restricted.",
|
||||||
|
@ -371,8 +370,8 @@ class DownloadUtils():
|
||||||
|
|
||||||
elif status not in ("401", "Auth"):
|
elif status not in ("401", "Auth"):
|
||||||
# Tell userclient token has been revoked.
|
# Tell userclient token has been revoked.
|
||||||
utils.window('emby_serverStatus', value="401")
|
window('emby_serverStatus', value="401")
|
||||||
self.logMsg("HTTP Error: %s" % e, 0)
|
log("HTTP Error: %s" % e, 0)
|
||||||
xbmcgui.Dialog().notification(
|
xbmcgui.Dialog().notification(
|
||||||
heading="Error connecting",
|
heading="Error connecting",
|
||||||
message="Unauthorized.",
|
message="Unauthorized.",
|
||||||
|
@ -387,11 +386,11 @@ class DownloadUtils():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
except requests.exceptions.SSLError as e:
|
except requests.exceptions.SSLError as e:
|
||||||
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
|
log("Invalid SSL certificate for: %s" % url, 0)
|
||||||
self.logMsg(e, 1)
|
log(e, 1)
|
||||||
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
self.logMsg("Unknown error connecting to: %s" % url, 0)
|
log("Unknown error connecting to: %s" % url, 0)
|
||||||
self.logMsg(e, 1)
|
log(e, 1)
|
||||||
|
|
||||||
return default_link
|
return default_link
|
|
@ -1,8 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
import utils
|
|
||||||
import xbmc
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from utils import Logging
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
|
||||||
class image_cache_thread(threading.Thread):
|
class image_cache_thread(threading.Thread):
|
||||||
|
|
||||||
urlToProcess = None
|
urlToProcess = None
|
||||||
|
@ -13,28 +19,32 @@ class image_cache_thread(threading.Thread):
|
||||||
xbmc_username = ""
|
xbmc_username = ""
|
||||||
xbmc_password = ""
|
xbmc_password = ""
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.monitor = xbmc.Monitor()
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s" % className, msg, lvl)
|
|
||||||
|
|
||||||
def setUrl(self, url):
|
def setUrl(self, url):
|
||||||
|
|
||||||
self.urlToProcess = url
|
self.urlToProcess = url
|
||||||
|
|
||||||
def setHost(self, host, port):
|
def setHost(self, host, port):
|
||||||
|
|
||||||
self.xbmc_host = host
|
self.xbmc_host = host
|
||||||
self.xbmc_port = port
|
self.xbmc_port = port
|
||||||
|
|
||||||
def setAuth(self, user, pwd):
|
def setAuth(self, user, pwd):
|
||||||
|
|
||||||
self.xbmc_username = user
|
self.xbmc_username = user
|
||||||
self.xbmc_password = pwd
|
self.xbmc_password = pwd
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
self.logMsg("Image Caching Thread Processing : " + self.urlToProcess, 2)
|
log("Image Caching Thread Processing: %s" % self.urlToProcess, 2)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.head(
|
response = requests.head(
|
||||||
|
@ -46,7 +56,5 @@ class image_cache_thread(threading.Thread):
|
||||||
# We don't need the result
|
# We don't need the result
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
self.logMsg("Image Caching Thread Exited", 2)
|
log("Image Caching Thread Exited", 2)
|
||||||
|
self.isFinished = True
|
||||||
self.isFinished = True
|
|
||||||
|
|
|
@ -9,10 +9,10 @@ import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
|
|
||||||
import utils
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import downloadutils
|
import downloadutils
|
||||||
import userclient
|
import userclient
|
||||||
|
from utils import Logging, settings, language as lang, passwordsXML
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -22,74 +22,67 @@ class InitialSetup():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self.addon = xbmcaddon.Addon()
|
global log
|
||||||
self.__language__ = self.addon.getLocalizedString
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
|
||||||
self.addonId = self.clientInfo.getAddonId()
|
self.addonId = self.clientInfo.getAddonId()
|
||||||
self.doUtils = downloadutils.DownloadUtils()
|
self.doUtils = downloadutils.DownloadUtils()
|
||||||
self.userClient = userclient.UserClient()
|
self.userClient = userclient.UserClient()
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
# Check server, user, direct paths, music, direct stream if not direct path.
|
# Check server, user, direct paths, music, direct stream if not direct path.
|
||||||
string = self.__language__
|
|
||||||
addonId = self.addonId
|
addonId = self.addonId
|
||||||
|
|
||||||
##### SERVER INFO #####
|
##### SERVER INFO #####
|
||||||
|
|
||||||
self.logMsg("Initial setup called.", 2)
|
log("Initial setup called.", 2)
|
||||||
server = self.userClient.getServer()
|
server = self.userClient.getServer()
|
||||||
|
|
||||||
if server:
|
if server:
|
||||||
self.logMsg("Server is already set.", 2)
|
log("Server is already set.", 2)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.logMsg("Looking for server...", 2)
|
log("Looking for server...", 2)
|
||||||
server = self.getServerDetails()
|
server = self.getServerDetails()
|
||||||
self.logMsg("Found: %s" % server, 2)
|
log("Found: %s" % server, 2)
|
||||||
try:
|
try:
|
||||||
prefix, ip, port = server.replace("/", "").split(":")
|
prefix, ip, port = server.replace("/", "").split(":")
|
||||||
except: # Failed to retrieve server information
|
except: # Failed to retrieve server information
|
||||||
self.logMsg("getServerDetails failed.", 1)
|
log("getServerDetails failed.", 1)
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
server_confirm = xbmcgui.Dialog().yesno(
|
server_confirm = xbmcgui.Dialog().yesno(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
line1="Proceed with the following server?",
|
line1="Proceed with the following server?",
|
||||||
line2="%s %s" % (string(30169), server))
|
line2="%s %s" % (lang(30169), server))
|
||||||
if server_confirm:
|
if server_confirm:
|
||||||
# Correct server found
|
# Correct server found
|
||||||
self.logMsg("Server is selected. Saving the information.", 1)
|
log("Server is selected. Saving the information.", 1)
|
||||||
utils.settings('ipaddress', value=ip)
|
settings('ipaddress', value=ip)
|
||||||
utils.settings('port', value=port)
|
settings('port', value=port)
|
||||||
|
|
||||||
if prefix == "https":
|
if prefix == "https":
|
||||||
utils.settings('https', value="true")
|
settings('https', value="true")
|
||||||
else:
|
else:
|
||||||
# User selected no or cancelled the dialog
|
# User selected no or cancelled the dialog
|
||||||
self.logMsg("No server selected.", 1)
|
log("No server selected.", 1)
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
||||||
return
|
return
|
||||||
|
|
||||||
##### USER INFO #####
|
##### USER INFO #####
|
||||||
|
|
||||||
self.logMsg("Getting user list.", 1)
|
log("Getting user list.", 1)
|
||||||
|
|
||||||
url = "%s/emby/Users/Public?format=json" % server
|
url = "%s/emby/Users/Public?format=json" % server
|
||||||
result = self.doUtils.downloadUrl(url, authenticate=False)
|
result = self.doUtils.downloadUrl(url, authenticate=False)
|
||||||
if result == "":
|
if result == "":
|
||||||
self.logMsg("Unable to connect to %s" % server, 1)
|
log("Unable to connect to %s" % server, 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.logMsg("Response: %s" % result, 2)
|
log("Response: %s" % result, 2)
|
||||||
# Process the list of users
|
# Process the list of users
|
||||||
usernames = []
|
usernames = []
|
||||||
users_hasPassword = []
|
users_hasPassword = []
|
||||||
|
@ -103,14 +96,14 @@ class InitialSetup():
|
||||||
name = "%s (secure)" % name
|
name = "%s (secure)" % name
|
||||||
users_hasPassword.append(name)
|
users_hasPassword.append(name)
|
||||||
|
|
||||||
self.logMsg("Presenting user list: %s" % users_hasPassword, 1)
|
log("Presenting user list: %s" % users_hasPassword, 1)
|
||||||
user_select = xbmcgui.Dialog().select(string(30200), users_hasPassword)
|
user_select = xbmcgui.Dialog().select(lang(30200), users_hasPassword)
|
||||||
if user_select > -1:
|
if user_select > -1:
|
||||||
selected_user = usernames[user_select]
|
selected_user = usernames[user_select]
|
||||||
self.logMsg("Selected user: %s" % selected_user, 1)
|
log("Selected user: %s" % selected_user, 1)
|
||||||
utils.settings('username', value=selected_user)
|
settings('username', value=selected_user)
|
||||||
else:
|
else:
|
||||||
self.logMsg("No user selected.", 1)
|
log("No user selected.", 1)
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
||||||
|
|
||||||
##### ADDITIONAL PROMPTS #####
|
##### ADDITIONAL PROMPTS #####
|
||||||
|
@ -126,8 +119,8 @@ class InitialSetup():
|
||||||
nolabel="Addon (Default)",
|
nolabel="Addon (Default)",
|
||||||
yeslabel="Native (Direct Paths)")
|
yeslabel="Native (Direct Paths)")
|
||||||
if directPaths:
|
if directPaths:
|
||||||
self.logMsg("User opted to use direct paths.", 1)
|
log("User opted to use direct paths.", 1)
|
||||||
utils.settings('useDirectPaths', value="1")
|
settings('useDirectPaths', value="1")
|
||||||
|
|
||||||
# ask for credentials
|
# ask for credentials
|
||||||
credentials = dialog.yesno(
|
credentials = dialog.yesno(
|
||||||
|
@ -138,15 +131,15 @@ class InitialSetup():
|
||||||
"during the initial scan of your content if Kodi can't "
|
"during the initial scan of your content if Kodi can't "
|
||||||
"locate your content."))
|
"locate your content."))
|
||||||
if credentials:
|
if credentials:
|
||||||
self.logMsg("Presenting network credentials dialog.", 1)
|
log("Presenting network credentials dialog.", 1)
|
||||||
utils.passwordsXML()
|
passwordsXML()
|
||||||
|
|
||||||
musicDisabled = dialog.yesno(
|
musicDisabled = dialog.yesno(
|
||||||
heading="Music Library",
|
heading="Music Library",
|
||||||
line1="Disable Emby music library?")
|
line1="Disable Emby music library?")
|
||||||
if musicDisabled:
|
if musicDisabled:
|
||||||
self.logMsg("User opted to disable Emby music library.", 1)
|
log("User opted to disable Emby music library.", 1)
|
||||||
utils.settings('enableMusic', value="false")
|
settings('enableMusic', value="false")
|
||||||
else:
|
else:
|
||||||
# Only prompt if the user didn't select direct paths for videos
|
# Only prompt if the user didn't select direct paths for videos
|
||||||
if not directPaths:
|
if not directPaths:
|
||||||
|
@ -157,12 +150,12 @@ class InitialSetup():
|
||||||
"this option only if you plan on listening "
|
"this option only if you plan on listening "
|
||||||
"to music outside of your network."))
|
"to music outside of your network."))
|
||||||
if musicAccess:
|
if musicAccess:
|
||||||
self.logMsg("User opted to direct stream music.", 1)
|
log("User opted to direct stream music.", 1)
|
||||||
utils.settings('streamMusic', value="true")
|
settings('streamMusic', value="true")
|
||||||
|
|
||||||
def getServerDetails(self):
|
def getServerDetails(self):
|
||||||
|
|
||||||
self.logMsg("Getting Server Details from Network", 1)
|
log("Getting Server Details from Network", 1)
|
||||||
|
|
||||||
MULTI_GROUP = ("<broadcast>", 7359)
|
MULTI_GROUP = ("<broadcast>", 7359)
|
||||||
MESSAGE = "who is EmbyServer?"
|
MESSAGE = "who is EmbyServer?"
|
||||||
|
@ -176,15 +169,15 @@ class InitialSetup():
|
||||||
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
|
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
|
||||||
sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)
|
sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)
|
||||||
|
|
||||||
self.logMsg("MultiGroup : %s" % str(MULTI_GROUP), 2)
|
log("MultiGroup : %s" % str(MULTI_GROUP), 2)
|
||||||
self.logMsg("Sending UDP Data: %s" % MESSAGE, 2)
|
log("Sending UDP Data: %s" % MESSAGE, 2)
|
||||||
sock.sendto(MESSAGE, MULTI_GROUP)
|
sock.sendto(MESSAGE, MULTI_GROUP)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
|
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
|
||||||
self.logMsg("Received Response: %s" % data)
|
log("Received Response: %s" % data)
|
||||||
except:
|
except:
|
||||||
self.logMsg("No UDP Response")
|
log("No UDP Response")
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# Get the address
|
# Get the address
|
||||||
|
|
|
@ -7,7 +7,7 @@ import xbmc
|
||||||
import api
|
import api
|
||||||
import artwork
|
import artwork
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import utils
|
from utils import Logging
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -19,16 +19,14 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
def __init__(self, cursor):
|
def __init__(self, cursor):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.cursor = cursor
|
self.cursor = cursor
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
self.artwork = artwork.Artwork()
|
self.artwork = artwork.Artwork()
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def addPath(self, path):
|
def addPath(self, path):
|
||||||
|
@ -153,7 +151,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO country(country_id, name) values(?, ?)"
|
query = "INSERT INTO country(country_id, name) values(?, ?)"
|
||||||
self.cursor.execute(query, (country_id, country))
|
self.cursor.execute(query, (country_id, country))
|
||||||
self.logMsg("Add country to media, processing: %s" % country, 2)
|
log("Add country to media, processing: %s" % country, 2)
|
||||||
|
|
||||||
finally: # Assign country to content
|
finally: # Assign country to content
|
||||||
query = (
|
query = (
|
||||||
|
@ -187,7 +185,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
|
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
|
||||||
self.cursor.execute(query, (idCountry, country))
|
self.cursor.execute(query, (idCountry, country))
|
||||||
self.logMsg("Add country to media, processing: %s" % country, 2)
|
log("Add country to media, processing: %s" % country, 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Only movies have a country field
|
# Only movies have a country field
|
||||||
|
@ -232,7 +230,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO actor(actor_id, name) values(?, ?)"
|
query = "INSERT INTO actor(actor_id, name) values(?, ?)"
|
||||||
self.cursor.execute(query, (actorid, name))
|
self.cursor.execute(query, (actorid, name))
|
||||||
self.logMsg("Add people to media, processing: %s" % name, 2)
|
log("Add people to media, processing: %s" % name, 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Link person to content
|
# Link person to content
|
||||||
|
@ -302,7 +300,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO actors(idActor, strActor) values(?, ?)"
|
query = "INSERT INTO actors(idActor, strActor) values(?, ?)"
|
||||||
self.cursor.execute(query, (actorid, name))
|
self.cursor.execute(query, (actorid, name))
|
||||||
self.logMsg("Add people to media, processing: %s" % name, 2)
|
log("Add people to media, processing: %s" % name, 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Link person to content
|
# Link person to content
|
||||||
|
@ -462,7 +460,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
|
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
|
||||||
self.cursor.execute(query, (genre_id, genre))
|
self.cursor.execute(query, (genre_id, genre))
|
||||||
self.logMsg("Add Genres to media, processing: %s" % genre, 2)
|
log("Add Genres to media, processing: %s" % genre, 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Assign genre to item
|
# Assign genre to item
|
||||||
|
@ -507,7 +505,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
||||||
self.cursor.execute(query, (idGenre, genre))
|
self.cursor.execute(query, (idGenre, genre))
|
||||||
self.logMsg("Add Genres to media, processing: %s" % genre, 2)
|
log("Add Genres to media, processing: %s" % genre, 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Assign genre to item
|
# Assign genre to item
|
||||||
|
@ -566,7 +564,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
|
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
|
||||||
self.cursor.execute(query, (studioid, studio))
|
self.cursor.execute(query, (studioid, studio))
|
||||||
self.logMsg("Add Studios to media, processing: %s" % studio, 2)
|
log("Add Studios to media, processing: %s" % studio, 2)
|
||||||
|
|
||||||
finally: # Assign studio to item
|
finally: # Assign studio to item
|
||||||
query = (
|
query = (
|
||||||
|
@ -597,7 +595,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)"
|
query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)"
|
||||||
self.cursor.execute(query, (studioid, studio))
|
self.cursor.execute(query, (studioid, studio))
|
||||||
self.logMsg("Add Studios to media, processing: %s" % studio, 2)
|
log("Add Studios to media, processing: %s" % studio, 2)
|
||||||
|
|
||||||
finally: # Assign studio to item
|
finally: # Assign studio to item
|
||||||
if "movie" in mediatype:
|
if "movie" in mediatype:
|
||||||
|
@ -728,7 +726,7 @@ class Kodidb_Functions():
|
||||||
self.cursor.execute(query, (kodiid, mediatype))
|
self.cursor.execute(query, (kodiid, mediatype))
|
||||||
|
|
||||||
# Add tags
|
# Add tags
|
||||||
self.logMsg("Adding Tags: %s" % tags, 2)
|
log("Adding Tags: %s" % tags, 2)
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
self.addTag(kodiid, tag, mediatype)
|
self.addTag(kodiid, tag, mediatype)
|
||||||
|
|
||||||
|
@ -750,7 +748,7 @@ class Kodidb_Functions():
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Create the tag, because it does not exist
|
# Create the tag, because it does not exist
|
||||||
tag_id = self.createTag(tag)
|
tag_id = self.createTag(tag)
|
||||||
self.logMsg("Adding tag: %s" % tag, 2)
|
log("Adding tag: %s" % tag, 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Assign tag to item
|
# Assign tag to item
|
||||||
|
@ -779,7 +777,7 @@ class Kodidb_Functions():
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Create the tag
|
# Create the tag
|
||||||
tag_id = self.createTag(tag)
|
tag_id = self.createTag(tag)
|
||||||
self.logMsg("Adding tag: %s" % tag, 2)
|
log("Adding tag: %s" % tag, 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Assign tag to item
|
# Assign tag to item
|
||||||
|
@ -815,7 +813,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
|
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
|
||||||
self.cursor.execute(query, (tag_id, name))
|
self.cursor.execute(query, (tag_id, name))
|
||||||
self.logMsg("Create tag_id: %s name: %s" % (tag_id, name), 2)
|
log("Create tag_id: %s name: %s" % (tag_id, name), 2)
|
||||||
else:
|
else:
|
||||||
# Kodi Helix
|
# Kodi Helix
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
@ -835,13 +833,13 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
|
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
|
||||||
self.cursor.execute(query, (tag_id, name))
|
self.cursor.execute(query, (tag_id, name))
|
||||||
self.logMsg("Create idTag: %s name: %s" % (tag_id, name), 2)
|
log("Create idTag: %s name: %s" % (tag_id, name), 2)
|
||||||
|
|
||||||
return tag_id
|
return tag_id
|
||||||
|
|
||||||
def updateTag(self, oldtag, newtag, kodiid, mediatype):
|
def updateTag(self, oldtag, newtag, kodiid, mediatype):
|
||||||
|
|
||||||
self.logMsg("Updating: %s with %s for %s: %s" % (oldtag, newtag, mediatype, kodiid), 2)
|
log("Updating: %s with %s for %s: %s" % (oldtag, newtag, mediatype, kodiid), 2)
|
||||||
|
|
||||||
if self.kodiversion in (15, 16, 17):
|
if self.kodiversion in (15, 16, 17):
|
||||||
# Kodi Isengard, Jarvis, Krypton
|
# Kodi Isengard, Jarvis, Krypton
|
||||||
|
@ -858,7 +856,7 @@ class Kodidb_Functions():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# The new tag we are going to apply already exists for this item
|
# The new tag we are going to apply already exists for this item
|
||||||
# delete current tag instead
|
# delete current tag instead
|
||||||
self.logMsg("Exception: %s" % e, 1)
|
log("Exception: %s" % e, 1)
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
"DELETE FROM tag_link",
|
"DELETE FROM tag_link",
|
||||||
|
@ -882,7 +880,7 @@ class Kodidb_Functions():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# The new tag we are going to apply already exists for this item
|
# The new tag we are going to apply already exists for this item
|
||||||
# delete current tag instead
|
# delete current tag instead
|
||||||
self.logMsg("Exception: %s" % e, 1)
|
log("Exception: %s" % e, 1)
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
"DELETE FROM taglinks",
|
"DELETE FROM taglinks",
|
||||||
|
@ -943,7 +941,7 @@ class Kodidb_Functions():
|
||||||
|
|
||||||
def createBoxset(self, boxsetname):
|
def createBoxset(self, boxsetname):
|
||||||
|
|
||||||
self.logMsg("Adding boxset: %s" % boxsetname, 2)
|
log("Adding boxset: %s" % boxsetname, 2)
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
"SELECT idSet",
|
"SELECT idSet",
|
||||||
|
|
|
@ -11,7 +11,7 @@ import clientinfo
|
||||||
import downloadutils
|
import downloadutils
|
||||||
import embydb_functions as embydb
|
import embydb_functions as embydb
|
||||||
import playbackutils as pbutils
|
import playbackutils as pbutils
|
||||||
import utils
|
from utils import Logging, window, settings, kodiSQL
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -21,27 +21,25 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
self.doUtils = downloadutils.DownloadUtils()
|
self.doUtils = downloadutils.DownloadUtils()
|
||||||
|
|
||||||
self.logMsg("Kodi monitor started.", 1)
|
log("Kodi monitor started.", 1)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
self.className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def onScanStarted(self, library):
|
def onScanStarted(self, library):
|
||||||
self.logMsg("Kodi library scan %s running." % library, 2)
|
log("Kodi library scan %s running." % library, 2)
|
||||||
if library == "video":
|
if library == "video":
|
||||||
utils.window('emby_kodiScan', value="true")
|
window('emby_kodiScan', value="true")
|
||||||
|
|
||||||
def onScanFinished(self, library):
|
def onScanFinished(self, library):
|
||||||
self.logMsg("Kodi library scan %s finished." % library, 2)
|
log("Kodi library scan %s finished." % library, 2)
|
||||||
if library == "video":
|
if library == "video":
|
||||||
utils.window('emby_kodiScan', clear=True)
|
window('emby_kodiScan', clear=True)
|
||||||
|
|
||||||
def onSettingsChanged(self):
|
def onSettingsChanged(self):
|
||||||
# Monitor emby settings
|
# Monitor emby settings
|
||||||
|
@ -50,7 +48,7 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
'''currentPath = utils.settings('useDirectPaths')
|
'''currentPath = utils.settings('useDirectPaths')
|
||||||
if utils.window('emby_pluginpath') != currentPath:
|
if utils.window('emby_pluginpath') != currentPath:
|
||||||
# Plugin path value changed. Offer to reset
|
# Plugin path value changed. Offer to reset
|
||||||
self.logMsg("Changed to playback mode detected", 1)
|
log("Changed to playback mode detected", 1)
|
||||||
utils.window('emby_pluginpath', value=currentPath)
|
utils.window('emby_pluginpath', value=currentPath)
|
||||||
resp = xbmcgui.Dialog().yesno(
|
resp = xbmcgui.Dialog().yesno(
|
||||||
heading="Playback mode change detected",
|
heading="Playback mode change detected",
|
||||||
|
@ -61,17 +59,17 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
if resp:
|
if resp:
|
||||||
utils.reset()'''
|
utils.reset()'''
|
||||||
|
|
||||||
currentLog = utils.settings('logLevel')
|
currentLog = settings('logLevel')
|
||||||
if utils.window('emby_logLevel') != currentLog:
|
if window('emby_logLevel') != currentLog:
|
||||||
# The log level changed, set new prop
|
# The log level changed, set new prop
|
||||||
self.logMsg("New log level: %s" % currentLog, 1)
|
log("New log level: %s" % currentLog, 1)
|
||||||
utils.window('emby_logLevel', value=currentLog)
|
window('emby_logLevel', value=currentLog)
|
||||||
|
|
||||||
def onNotification(self, sender, method, data):
|
def onNotification(self, sender, method, data):
|
||||||
|
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
if method not in ("Playlist.OnAdd"):
|
if method not in ("Playlist.OnAdd"):
|
||||||
self.logMsg("Method: %s Data: %s" % (method, data), 1)
|
log("Method: %s Data: %s" % (method, data), 1)
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
data = json.loads(data,'utf-8')
|
data = json.loads(data,'utf-8')
|
||||||
|
@ -84,23 +82,23 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
kodiid = item['id']
|
kodiid = item['id']
|
||||||
item_type = item['type']
|
item_type = item['type']
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Item is invalid for playstate update.", 1)
|
log("Item is invalid for playstate update.", 1)
|
||||||
else:
|
else:
|
||||||
if ((utils.settings('useDirectPaths') == "1" and not item_type == "song") or
|
if ((settings('useDirectPaths') == "1" and not item_type == "song") or
|
||||||
(item_type == "song" and utils.settings('enableMusic') == "true")):
|
(item_type == "song" and settings('enableMusic') == "true")):
|
||||||
# Set up properties for player
|
# Set up properties for player
|
||||||
embyconn = utils.kodiSQL('emby')
|
embyconn = kodiSQL('emby')
|
||||||
embycursor = embyconn.cursor()
|
embycursor = embyconn.cursor()
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
|
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
|
||||||
try:
|
try:
|
||||||
itemid = emby_dbitem[0]
|
itemid = emby_dbitem[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("No kodiid returned.", 1)
|
log("No kodiid returned.", 1)
|
||||||
else:
|
else:
|
||||||
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
|
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils.downloadUrl(url)
|
||||||
self.logMsg("Item: %s" % result, 2)
|
log("Item: %s" % result, 2)
|
||||||
|
|
||||||
playurl = None
|
playurl = None
|
||||||
count = 0
|
count = 0
|
||||||
|
@ -114,12 +112,10 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
listItem = xbmcgui.ListItem()
|
listItem = xbmcgui.ListItem()
|
||||||
playback = pbutils.PlaybackUtils(result)
|
playback = pbutils.PlaybackUtils(result)
|
||||||
|
|
||||||
if item_type == "song" and utils.settings('streamMusic') == "true":
|
if item_type == "song" and settings('streamMusic') == "true":
|
||||||
utils.window('emby_%s.playmethod' % playurl,
|
window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||||
value="DirectStream")
|
|
||||||
else:
|
else:
|
||||||
utils.window('emby_%s.playmethod' % playurl,
|
window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
||||||
value="DirectPlay")
|
|
||||||
# Set properties for player.py
|
# Set properties for player.py
|
||||||
playback.setProperties(playurl, listItem)
|
playback.setProperties(playurl, listItem)
|
||||||
finally:
|
finally:
|
||||||
|
@ -134,31 +130,31 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
kodiid = item['id']
|
kodiid = item['id']
|
||||||
item_type = item['type']
|
item_type = item['type']
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Item is invalid for playstate update.", 1)
|
log("Item is invalid for playstate update.", 1)
|
||||||
else:
|
else:
|
||||||
# Send notification to the server.
|
# Send notification to the server.
|
||||||
embyconn = utils.kodiSQL('emby')
|
embyconn = kodiSQL('emby')
|
||||||
embycursor = embyconn.cursor()
|
embycursor = embyconn.cursor()
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
|
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
|
||||||
try:
|
try:
|
||||||
itemid = emby_dbitem[0]
|
itemid = emby_dbitem[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("Could not find itemid in emby database.", 1)
|
log("Could not find itemid in emby database.", 1)
|
||||||
else:
|
else:
|
||||||
# Stop from manually marking as watched unwatched, with actual playback.
|
# Stop from manually marking as watched unwatched, with actual playback.
|
||||||
if utils.window('emby_skipWatched%s' % itemid) == "true":
|
if window('emby_skipWatched%s' % itemid) == "true":
|
||||||
# property is set in player.py
|
# property is set in player.py
|
||||||
utils.window('emby_skipWatched%s' % itemid, clear=True)
|
window('emby_skipWatched%s' % itemid, clear=True)
|
||||||
else:
|
else:
|
||||||
# notify the server
|
# notify the server
|
||||||
url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % itemid
|
url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % itemid
|
||||||
if playcount != 0:
|
if playcount != 0:
|
||||||
doUtils.downloadUrl(url, action_type="POST")
|
doUtils.downloadUrl(url, action_type="POST")
|
||||||
self.logMsg("Mark as watched for itemid: %s" % itemid, 1)
|
log("Mark as watched for itemid: %s" % itemid, 1)
|
||||||
else:
|
else:
|
||||||
doUtils.downloadUrl(url, action_type="DELETE")
|
doUtils.downloadUrl(url, action_type="DELETE")
|
||||||
self.logMsg("Mark as unwatched for itemid: %s" % itemid, 1)
|
log("Mark as unwatched for itemid: %s" % itemid, 1)
|
||||||
finally:
|
finally:
|
||||||
embycursor.close()
|
embycursor.close()
|
||||||
|
|
||||||
|
@ -172,7 +168,7 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
kodiid = data['id']
|
kodiid = data['id']
|
||||||
type = data['type']
|
type = data['type']
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Item is invalid for emby deletion.", 1)
|
log("Item is invalid for emby deletion.", 1)
|
||||||
else:
|
else:
|
||||||
# Send the delete action to the server.
|
# Send the delete action to the server.
|
||||||
embyconn = utils.kodiSQL('emby')
|
embyconn = utils.kodiSQL('emby')
|
||||||
|
@ -182,19 +178,19 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
try:
|
try:
|
||||||
itemid = emby_dbitem[0]
|
itemid = emby_dbitem[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("Could not find itemid in emby database.", 1)
|
log("Could not find itemid in emby database.", 1)
|
||||||
else:
|
else:
|
||||||
if utils.settings('skipContextMenu') != "true":
|
if utils.settings('skipContextMenu') != "true":
|
||||||
resp = xbmcgui.Dialog().yesno(
|
resp = xbmcgui.Dialog().yesno(
|
||||||
heading="Confirm delete",
|
heading="Confirm delete",
|
||||||
line1="Delete file on Emby Server?")
|
line1="Delete file on Emby Server?")
|
||||||
if not resp:
|
if not resp:
|
||||||
self.logMsg("User skipped deletion.", 1)
|
log("User skipped deletion.", 1)
|
||||||
embycursor.close()
|
embycursor.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||||
self.logMsg("Deleting request: %s" % itemid)
|
log("Deleting request: %s" % itemid)
|
||||||
doUtils.downloadUrl(url, action_type="DELETE")
|
doUtils.downloadUrl(url, action_type="DELETE")
|
||||||
finally:
|
finally:
|
||||||
embycursor.close()'''
|
embycursor.close()'''
|
||||||
|
@ -203,13 +199,13 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
elif method == "System.OnWake":
|
elif method == "System.OnWake":
|
||||||
# Allow network to wake up
|
# Allow network to wake up
|
||||||
xbmc.sleep(10000)
|
xbmc.sleep(10000)
|
||||||
utils.window('emby_onWake', value="true")
|
window('emby_onWake', value="true")
|
||||||
|
|
||||||
|
|
||||||
elif method == "GUI.OnScreensaverDeactivated":
|
elif method == "GUI.OnScreensaverDeactivated":
|
||||||
if utils.settings('dbSyncScreensaver') == "true":
|
if settings('dbSyncScreensaver') == "true":
|
||||||
xbmc.sleep(5000);
|
xbmc.sleep(5000);
|
||||||
utils.window('emby_onWake', value="true")
|
window('emby_onWake', value="true")
|
||||||
|
|
||||||
|
|
||||||
elif method == "Playlist.OnClear":
|
elif method == "Playlist.OnClear":
|
||||||
|
|
|
@ -20,6 +20,7 @@ import kodidb_functions as kodidb
|
||||||
import read_embyserver as embyserver
|
import read_embyserver as embyserver
|
||||||
import userclient
|
import userclient
|
||||||
import videonodes
|
import videonodes
|
||||||
|
from utils import Logging, window, settings, language as lang
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -42,6 +43,9 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
self.monitor = xbmc.Monitor()
|
self.monitor = xbmc.Monitor()
|
||||||
|
|
||||||
|
@ -54,26 +58,20 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def progressDialog(self, title, forced=False):
|
def progressDialog(self, title, forced=False):
|
||||||
|
|
||||||
dialog = None
|
dialog = None
|
||||||
|
|
||||||
if utils.settings('dbSyncIndicator') == "true" or forced:
|
if settings('dbSyncIndicator') == "true" or forced:
|
||||||
dialog = xbmcgui.DialogProgressBG()
|
dialog = xbmcgui.DialogProgressBG()
|
||||||
dialog.create("Emby for Kodi", title)
|
dialog.create("Emby for Kodi", title)
|
||||||
self.logMsg("Show progress dialog: %s" % title, 2)
|
log("Show progress dialog: %s" % title, 2)
|
||||||
|
|
||||||
return dialog
|
return dialog
|
||||||
|
|
||||||
def startSync(self):
|
def startSync(self):
|
||||||
|
|
||||||
settings = utils.settings
|
|
||||||
# Run at start up - optional to use the server plugin
|
# Run at start up - optional to use the server plugin
|
||||||
if settings('SyncInstallRunDone') == "true":
|
if settings('SyncInstallRunDone') == "true":
|
||||||
|
|
||||||
|
@ -88,7 +86,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
for plugin in result:
|
for plugin in result:
|
||||||
if plugin['Name'] == "Emby.Kodi Sync Queue":
|
if plugin['Name'] == "Emby.Kodi Sync Queue":
|
||||||
self.logMsg("Found server plugin.", 2)
|
log("Found server plugin.", 2)
|
||||||
completed = self.fastSync()
|
completed = self.fastSync()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -103,37 +101,31 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
def fastSync(self):
|
def fastSync(self):
|
||||||
|
|
||||||
lastSync = utils.settings('LastIncrementalSync')
|
lastSync = settings('LastIncrementalSync')
|
||||||
if not lastSync:
|
if not lastSync:
|
||||||
lastSync = "2010-01-01T00:00:00Z"
|
lastSync = "2010-01-01T00:00:00Z"
|
||||||
|
|
||||||
lastSyncTime = utils.convertdate(lastSync)
|
lastSyncTime = utils.convertDate(lastSync)
|
||||||
self.logMsg("Last sync run: %s" % lastSyncTime, 1)
|
log("Last sync run: %s" % lastSyncTime, 1)
|
||||||
|
|
||||||
# get server RetentionDateTime
|
# get server RetentionDateTime
|
||||||
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json")
|
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json")
|
||||||
retention_time = "2010-01-01T00:00:00Z"
|
|
||||||
if result and result.get('RetentionDateTime'):
|
|
||||||
retention_time = result['RetentionDateTime']
|
|
||||||
|
|
||||||
#Try/except equivalent
|
|
||||||
'''
|
|
||||||
try:
|
try:
|
||||||
retention_time = result['RetentionDateTime']
|
retention_time = result['RetentionDateTime']
|
||||||
except (TypeError, KeyError):
|
except (TypeError, KeyError):
|
||||||
retention_time = "2010-01-01T00:00:00Z"
|
retention_time = "2010-01-01T00:00:00Z"
|
||||||
'''
|
|
||||||
|
|
||||||
retention_time = utils.convertdate(retention_time)
|
retention_time = utils.convertDate(retention_time)
|
||||||
self.logMsg("RetentionDateTime: %s" % retention_time, 1)
|
log("RetentionDateTime: %s" % retention_time, 1)
|
||||||
|
|
||||||
# if last sync before retention time do a full sync
|
# if last sync before retention time do a full sync
|
||||||
if retention_time > lastSyncTime:
|
if retention_time > lastSyncTime:
|
||||||
self.logMsg("Fast sync server retention insufficient, fall back to full sync", 1)
|
log("Fast sync server retention insufficient, fall back to full sync", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
params = {'LastUpdateDT': lastSync}
|
params = {'LastUpdateDT': lastSync}
|
||||||
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json", parameters=params)
|
url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json"
|
||||||
|
result = self.doUtils(url, parameters=params)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
processlist = {
|
processlist = {
|
||||||
|
@ -145,11 +137,11 @@ class LibrarySync(threading.Thread):
|
||||||
}
|
}
|
||||||
|
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Failed to retrieve latest updates using fast sync.", 1)
|
log("Failed to retrieve latest updates using fast sync.", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg("Fast sync changes: %s" % result, 1)
|
log("Fast sync changes: %s" % result, 1)
|
||||||
for action in processlist:
|
for action in processlist:
|
||||||
self.triage_items(action, processlist[action])
|
self.triage_items(action, processlist[action])
|
||||||
|
|
||||||
|
@ -163,60 +155,55 @@ class LibrarySync(threading.Thread):
|
||||||
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json")
|
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json")
|
||||||
try: # datetime fails when used more than once, TypeError
|
try: # datetime fails when used more than once, TypeError
|
||||||
server_time = result['ServerDateTime']
|
server_time = result['ServerDateTime']
|
||||||
server_time = utils.convertdate(server_time)
|
server_time = utils.convertDate(server_time)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If the server plugin is not installed or an error happened.
|
# If the server plugin is not installed or an error happened.
|
||||||
self.logMsg("An exception occurred: %s" % e, 1)
|
log("An exception occurred: %s" % e, 1)
|
||||||
time_now = datetime.utcnow()-timedelta(minutes=overlap)
|
time_now = datetime.utcnow()-timedelta(minutes=overlap)
|
||||||
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
|
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
self.logMsg("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
|
log("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
|
lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
self.logMsg("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
|
log("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
utils.settings('LastIncrementalSync', value=lastSync)
|
settings('LastIncrementalSync', value=lastSync)
|
||||||
|
|
||||||
def shouldStop(self):
|
def shouldStop(self):
|
||||||
# Checkpoint during the syncing process
|
# Checkpoint during the syncing process
|
||||||
if self.monitor.abortRequested():
|
if self.monitor.abortRequested():
|
||||||
return True
|
return True
|
||||||
elif utils.window('emby_shouldStop') == "true":
|
elif window('emby_shouldStop') == "true":
|
||||||
return True
|
return True
|
||||||
else: # Keep going
|
else: # Keep going
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def dbCommit(self, connection):
|
def dbCommit(self, connection):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
# Central commit, verifies if Kodi database update is running
|
# Central commit, verifies if Kodi database update is running
|
||||||
kodidb_scan = window('emby_kodiScan') == "true"
|
kodidb_scan = window('emby_kodiScan') == "true"
|
||||||
|
|
||||||
while kodidb_scan:
|
while kodidb_scan:
|
||||||
|
|
||||||
self.logMsg("Kodi scan is running. Waiting...", 1)
|
log("Kodi scan is running. Waiting...", 1)
|
||||||
kodidb_scan = window('emby_kodiScan') == "true"
|
kodidb_scan = window('emby_kodiScan') == "true"
|
||||||
|
|
||||||
if self.shouldStop():
|
if self.shouldStop():
|
||||||
self.logMsg("Commit unsuccessful. Sync terminated.", 1)
|
log("Commit unsuccessful. Sync terminated.", 1)
|
||||||
break
|
break
|
||||||
|
|
||||||
if self.monitor.waitForAbort(1):
|
if self.monitor.waitForAbort(1):
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
self.logMsg("Commit unsuccessful.", 1)
|
log("Commit unsuccessful.", 1)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
connection.commit()
|
connection.commit()
|
||||||
self.logMsg("Commit successful.", 1)
|
log("Commit successful.", 1)
|
||||||
|
|
||||||
def fullSync(self, manualrun=False, repair=False, forceddialog=False):
|
def fullSync(self, manualrun=False, repair=False, forceddialog=False):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
settings = utils.settings
|
|
||||||
# Only run once when first setting up. Can be run manually.
|
# Only run once when first setting up. Can be run manually.
|
||||||
music_enabled = utils.settings('enableMusic') == "true"
|
music_enabled = settings('enableMusic') == "true"
|
||||||
|
|
||||||
xbmc.executebuiltin('InhibitIdleShutdown(true)')
|
xbmc.executebuiltin('InhibitIdleShutdown(true)')
|
||||||
screensaver = utils.getScreensaver()
|
screensaver = utils.getScreensaver()
|
||||||
|
@ -284,7 +271,7 @@ class LibrarySync(threading.Thread):
|
||||||
self.dbCommit(kodiconn)
|
self.dbCommit(kodiconn)
|
||||||
embyconn.commit()
|
embyconn.commit()
|
||||||
elapsedTime = datetime.now() - startTime
|
elapsedTime = datetime.now() - startTime
|
||||||
self.logMsg("SyncDatabase (finished %s in: %s)"
|
log("SyncDatabase (finished %s in: %s)"
|
||||||
% (itemtype, str(elapsedTime).split('.')[0]), 1)
|
% (itemtype, str(elapsedTime).split('.')[0]), 1)
|
||||||
else:
|
else:
|
||||||
# Close the Kodi cursor
|
# Close the Kodi cursor
|
||||||
|
@ -312,7 +299,7 @@ class LibrarySync(threading.Thread):
|
||||||
musicconn.commit()
|
musicconn.commit()
|
||||||
embyconn.commit()
|
embyconn.commit()
|
||||||
elapsedTime = datetime.now() - startTime
|
elapsedTime = datetime.now() - startTime
|
||||||
self.logMsg("SyncDatabase (finished music in: %s)"
|
log("SyncDatabase (finished music in: %s)"
|
||||||
% (str(elapsedTime).split('.')[0]), 1)
|
% (str(elapsedTime).split('.')[0]), 1)
|
||||||
musiccursor.close()
|
musiccursor.close()
|
||||||
|
|
||||||
|
@ -335,7 +322,7 @@ class LibrarySync(threading.Thread):
|
||||||
xbmcgui.Dialog().notification(
|
xbmcgui.Dialog().notification(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="%s %s %s" %
|
message="%s %s %s" %
|
||||||
(message, utils.language(33025), str(elapsedtotal).split('.')[0]),
|
(message, lang(33025), str(elapsedtotal).split('.')[0]),
|
||||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||||
sound=False)
|
sound=False)
|
||||||
return True
|
return True
|
||||||
|
@ -378,7 +365,7 @@ class LibrarySync(threading.Thread):
|
||||||
if view['type'] == "mixed":
|
if view['type'] == "mixed":
|
||||||
sorted_views.append(view['name'])
|
sorted_views.append(view['name'])
|
||||||
sorted_views.append(view['name'])
|
sorted_views.append(view['name'])
|
||||||
self.logMsg("Sorted views: %s" % sorted_views, 1)
|
log("Sorted views: %s" % sorted_views, 1)
|
||||||
|
|
||||||
# total nodes for window properties
|
# total nodes for window properties
|
||||||
self.vnodes.clearProperties()
|
self.vnodes.clearProperties()
|
||||||
|
@ -415,7 +402,8 @@ class LibrarySync(threading.Thread):
|
||||||
'Limit': 1,
|
'Limit': 1,
|
||||||
'IncludeItemTypes': emby_mediatypes[mediatype]
|
'IncludeItemTypes': emby_mediatypes[mediatype]
|
||||||
} # Get one item from server using the folderid
|
} # Get one item from server using the folderid
|
||||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
url = "{server}/emby/Users/{UserId}/Items?format=json"
|
||||||
|
result = self.doUtils(url, parameters=params)
|
||||||
try:
|
try:
|
||||||
verifyitem = result['Items'][0]['Id']
|
verifyitem = result['Items'][0]['Id']
|
||||||
except (TypeError, IndexError):
|
except (TypeError, IndexError):
|
||||||
|
@ -430,14 +418,14 @@ class LibrarySync(threading.Thread):
|
||||||
# Take the userview, and validate the item belong to the view
|
# Take the userview, and validate the item belong to the view
|
||||||
if self.emby.verifyView(grouped_view['Id'], verifyitem):
|
if self.emby.verifyView(grouped_view['Id'], verifyitem):
|
||||||
# Take the name of the userview
|
# Take the name of the userview
|
||||||
self.logMsg("Found corresponding view: %s %s"
|
log("Found corresponding view: %s %s"
|
||||||
% (grouped_view['Name'], grouped_view['Id']), 1)
|
% (grouped_view['Name'], grouped_view['Id']), 1)
|
||||||
foldername = grouped_view['Name']
|
foldername = grouped_view['Name']
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# Unable to find a match, add the name to our sorted_view list
|
# Unable to find a match, add the name to our sorted_view list
|
||||||
sorted_views.append(foldername)
|
sorted_views.append(foldername)
|
||||||
self.logMsg("Couldn't find corresponding grouped view: %s" % sorted_views, 1)
|
log("Couldn't find corresponding grouped view: %s" % sorted_views, 1)
|
||||||
|
|
||||||
# Failsafe
|
# Failsafe
|
||||||
try:
|
try:
|
||||||
|
@ -453,7 +441,7 @@ class LibrarySync(threading.Thread):
|
||||||
current_tagid = view[2]
|
current_tagid = view[2]
|
||||||
|
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("Creating viewid: %s in Emby database." % folderid, 1)
|
log("Creating viewid: %s in Emby database." % folderid, 1)
|
||||||
tagid = kodi_db.createTag(foldername)
|
tagid = kodi_db.createTag(foldername)
|
||||||
# Create playlist for the video library
|
# Create playlist for the video library
|
||||||
if (foldername not in playlists and
|
if (foldername not in playlists and
|
||||||
|
@ -472,7 +460,7 @@ class LibrarySync(threading.Thread):
|
||||||
emby_db.addView(folderid, foldername, viewtype, tagid)
|
emby_db.addView(folderid, foldername, viewtype, tagid)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg(' '.join((
|
log(' '.join((
|
||||||
|
|
||||||
"Found viewid: %s" % folderid,
|
"Found viewid: %s" % folderid,
|
||||||
"viewname: %s" % current_viewname,
|
"viewname: %s" % current_viewname,
|
||||||
|
@ -488,7 +476,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
# View was modified, update with latest info
|
# View was modified, update with latest info
|
||||||
if current_viewname != foldername:
|
if current_viewname != foldername:
|
||||||
self.logMsg("viewid: %s new viewname: %s" % (folderid, foldername), 1)
|
log("viewid: %s new viewname: %s" % (folderid, foldername), 1)
|
||||||
tagid = kodi_db.createTag(foldername)
|
tagid = kodi_db.createTag(foldername)
|
||||||
|
|
||||||
# Update view with new info
|
# Update view with new info
|
||||||
|
@ -556,20 +544,19 @@ class LibrarySync(threading.Thread):
|
||||||
utils.window('Emby.nodes.total', str(totalnodes))
|
utils.window('Emby.nodes.total', str(totalnodes))
|
||||||
|
|
||||||
# Remove any old referenced views
|
# Remove any old referenced views
|
||||||
self.logMsg("Removing views: %s" % current_views, 1)
|
log("Removing views: %s" % current_views, 1)
|
||||||
for view in current_views:
|
for view in current_views:
|
||||||
emby_db.removeView(view)
|
emby_db.removeView(view)
|
||||||
|
|
||||||
def movies(self, embycursor, kodicursor, pdialog):
|
def movies(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
lang = utils.language
|
|
||||||
# Get movies from emby
|
# Get movies from emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
movies = itemtypes.Movies(embycursor, kodicursor)
|
movies = itemtypes.Movies(embycursor, kodicursor)
|
||||||
|
|
||||||
views = emby_db.getView_byType('movies')
|
views = emby_db.getView_byType('movies')
|
||||||
views += emby_db.getView_byType('mixed')
|
views += emby_db.getView_byType('mixed')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
##### PROCESS MOVIES #####
|
##### PROCESS MOVIES #####
|
||||||
for view in views:
|
for view in views:
|
||||||
|
@ -604,7 +591,7 @@ class LibrarySync(threading.Thread):
|
||||||
count += 1
|
count += 1
|
||||||
movies.add_update(embymovie, view['name'], view['id'])
|
movies.add_update(embymovie, view['name'], view['id'])
|
||||||
else:
|
else:
|
||||||
self.logMsg("Movies finished.", 2)
|
log("Movies finished.", 2)
|
||||||
|
|
||||||
|
|
||||||
##### PROCESS BOXSETS #####
|
##### PROCESS BOXSETS #####
|
||||||
|
@ -631,7 +618,7 @@ class LibrarySync(threading.Thread):
|
||||||
count += 1
|
count += 1
|
||||||
movies.add_updateBoxset(boxset)
|
movies.add_updateBoxset(boxset)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Boxsets finished.", 2)
|
log("Boxsets finished.", 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -642,7 +629,7 @@ class LibrarySync(threading.Thread):
|
||||||
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
||||||
|
|
||||||
views = emby_db.getView_byType('musicvideos')
|
views = emby_db.getView_byType('musicvideos')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
for view in views:
|
for view in views:
|
||||||
|
|
||||||
|
@ -656,7 +643,7 @@ class LibrarySync(threading.Thread):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="%s %s..." % (utils.language(33019), viewName))
|
message="%s %s..." % (lang(33019), viewName))
|
||||||
|
|
||||||
# Initial or repair sync
|
# Initial or repair sync
|
||||||
all_embymvideos = self.emby.getMusicVideos(viewId, dialog=pdialog)
|
all_embymvideos = self.emby.getMusicVideos(viewId, dialog=pdialog)
|
||||||
|
@ -679,7 +666,7 @@ class LibrarySync(threading.Thread):
|
||||||
count += 1
|
count += 1
|
||||||
mvideos.add_update(embymvideo, viewName, viewId)
|
mvideos.add_update(embymvideo, viewName, viewId)
|
||||||
else:
|
else:
|
||||||
self.logMsg("MusicVideos finished.", 2)
|
log("MusicVideos finished.", 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -691,7 +678,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
views = emby_db.getView_byType('tvshows')
|
views = emby_db.getView_byType('tvshows')
|
||||||
views += emby_db.getView_byType('mixed')
|
views += emby_db.getView_byType('mixed')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
for view in views:
|
for view in views:
|
||||||
|
|
||||||
|
@ -702,7 +689,7 @@ class LibrarySync(threading.Thread):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="%s %s..." % (utils.language(33020), view['name']))
|
message="%s %s..." % (lang(33020), view['name']))
|
||||||
|
|
||||||
all_embytvshows = self.emby.getShows(view['id'], dialog=pdialog)
|
all_embytvshows = self.emby.getShows(view['id'], dialog=pdialog)
|
||||||
total = all_embytvshows['TotalRecordCount']
|
total = all_embytvshows['TotalRecordCount']
|
||||||
|
@ -737,7 +724,7 @@ class LibrarySync(threading.Thread):
|
||||||
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
|
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
|
||||||
tvshows.add_updateEpisode(episode)
|
tvshows.add_updateEpisode(episode)
|
||||||
else:
|
else:
|
||||||
self.logMsg("TVShows finished.", 2)
|
log("TVShows finished.", 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -757,7 +744,7 @@ class LibrarySync(threading.Thread):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="%s %s..." % (utils.language(33021), itemtype))
|
message="%s %s..." % (lang(33021), itemtype))
|
||||||
|
|
||||||
all_embyitems = process[itemtype][0](dialog=pdialog)
|
all_embyitems = process[itemtype][0](dialog=pdialog)
|
||||||
total = all_embyitems['TotalRecordCount']
|
total = all_embyitems['TotalRecordCount']
|
||||||
|
@ -778,7 +765,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
process[itemtype][1](embyitem)
|
process[itemtype][1](embyitem)
|
||||||
else:
|
else:
|
||||||
self.logMsg("%s finished." % itemtype, 2)
|
log("%s finished." % itemtype, 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -799,7 +786,7 @@ class LibrarySync(threading.Thread):
|
||||||
itemids.append(item['ItemId'])
|
itemids.append(item['ItemId'])
|
||||||
items = itemids
|
items = itemids
|
||||||
|
|
||||||
self.logMsg("Queue %s: %s" % (process, items), 1)
|
log("Queue %s: %s" % (process, items), 1)
|
||||||
processlist[process].extend(items)
|
processlist[process].extend(items)
|
||||||
|
|
||||||
def incrementalSync(self):
|
def incrementalSync(self):
|
||||||
|
@ -833,7 +820,7 @@ class LibrarySync(threading.Thread):
|
||||||
}
|
}
|
||||||
for process_type in ['added', 'update', 'userdata', 'remove']:
|
for process_type in ['added', 'update', 'userdata', 'remove']:
|
||||||
|
|
||||||
if process[process_type] and utils.window('emby_kodiScan') != "true":
|
if process[process_type] and window('emby_kodiScan') != "true":
|
||||||
|
|
||||||
listItems = list(process[process_type])
|
listItems = list(process[process_type])
|
||||||
del process[process_type][:] # Reset class list
|
del process[process_type][:] # Reset class list
|
||||||
|
@ -871,7 +858,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
if update_embydb:
|
if update_embydb:
|
||||||
update_embydb = False
|
update_embydb = False
|
||||||
self.logMsg("Updating emby database.", 1)
|
log("Updating emby database.", 1)
|
||||||
embyconn.commit()
|
embyconn.commit()
|
||||||
self.saveLastSync()
|
self.saveLastSync()
|
||||||
|
|
||||||
|
@ -880,8 +867,8 @@ class LibrarySync(threading.Thread):
|
||||||
self.forceLibraryUpdate = False
|
self.forceLibraryUpdate = False
|
||||||
self.dbCommit(kodiconn)
|
self.dbCommit(kodiconn)
|
||||||
|
|
||||||
self.logMsg("Updating video library.", 1)
|
log("Updating video library.", 1)
|
||||||
utils.window('emby_kodiScan', value="true")
|
window('emby_kodiScan', value="true")
|
||||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||||
|
|
||||||
if pDialog:
|
if pDialog:
|
||||||
|
@ -893,7 +880,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
def compareDBVersion(self, current, minimum):
|
def compareDBVersion(self, current, minimum):
|
||||||
# It returns True is database is up to date. False otherwise.
|
# It returns True is database is up to date. False otherwise.
|
||||||
self.logMsg("current: %s minimum: %s" % (current, minimum), 1)
|
log("current: %s minimum: %s" % (current, minimum), 1)
|
||||||
currMajor, currMinor, currPatch = current.split(".")
|
currMajor, currMinor, currPatch = current.split(".")
|
||||||
minMajor, minMinor, minPatch = minimum.split(".")
|
minMajor, minMinor, minPatch = minimum.split(".")
|
||||||
|
|
||||||
|
@ -911,7 +898,7 @@ class LibrarySync(threading.Thread):
|
||||||
try:
|
try:
|
||||||
self.run_internal()
|
self.run_internal()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
utils.window('emby_dbScan', clear=True)
|
window('emby_dbScan', clear=True)
|
||||||
xbmcgui.Dialog().ok(
|
xbmcgui.Dialog().ok(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
line1=(
|
line1=(
|
||||||
|
@ -922,14 +909,11 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
def run_internal(self):
|
def run_internal(self):
|
||||||
|
|
||||||
lang = utils.language
|
|
||||||
window = utils.window
|
|
||||||
settings = utils.settings
|
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
||||||
startupComplete = False
|
startupComplete = False
|
||||||
|
|
||||||
self.logMsg("---===### Starting LibrarySync ###===---", 0)
|
log("---===### Starting LibrarySync ###===---", 0)
|
||||||
|
|
||||||
while not self.monitor.abortRequested():
|
while not self.monitor.abortRequested():
|
||||||
|
|
||||||
|
@ -947,12 +931,12 @@ class LibrarySync(threading.Thread):
|
||||||
uptoDate = self.compareDBVersion(currentVersion, minVersion)
|
uptoDate = self.compareDBVersion(currentVersion, minVersion)
|
||||||
|
|
||||||
if not uptoDate:
|
if not uptoDate:
|
||||||
self.logMsg("Database version out of date: %s minimum version required: %s"
|
log("Database version out of date: %s minimum version required: %s"
|
||||||
% (currentVersion, minVersion), 0)
|
% (currentVersion, minVersion), 0)
|
||||||
|
|
||||||
resp = dialog.yesno("Emby for Kodi", lang(33022))
|
resp = dialog.yesno("Emby for Kodi", lang(33022))
|
||||||
if not resp:
|
if not resp:
|
||||||
self.logMsg("Database version is out of date! USER IGNORED!", 0)
|
log("Database version is out of date! USER IGNORED!", 0)
|
||||||
dialog.ok("Emby for Kodi", lang(33023))
|
dialog.ok("Emby for Kodi", lang(33023))
|
||||||
else:
|
else:
|
||||||
utils.reset()
|
utils.reset()
|
||||||
|
@ -967,7 +951,7 @@ class LibrarySync(threading.Thread):
|
||||||
videoDb = utils.getKodiVideoDBPath()
|
videoDb = utils.getKodiVideoDBPath()
|
||||||
if not xbmcvfs.exists(videoDb):
|
if not xbmcvfs.exists(videoDb):
|
||||||
# Database does not exists
|
# Database does not exists
|
||||||
self.logMsg(
|
log(
|
||||||
"The current Kodi version is incompatible "
|
"The current Kodi version is incompatible "
|
||||||
"with the Emby for Kodi add-on. Please visit "
|
"with the Emby for Kodi add-on. Please visit "
|
||||||
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
|
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
|
||||||
|
@ -979,12 +963,12 @@ class LibrarySync(threading.Thread):
|
||||||
break
|
break
|
||||||
|
|
||||||
# Run start up sync
|
# Run start up sync
|
||||||
self.logMsg("Database version: %s" % settings('dbCreatedWithVersion'), 0)
|
log("Database version: %s" % settings('dbCreatedWithVersion'), 0)
|
||||||
self.logMsg("SyncDatabase (started)", 1)
|
log("SyncDatabase (started)", 1)
|
||||||
startTime = datetime.now()
|
startTime = datetime.now()
|
||||||
librarySync = self.startSync()
|
librarySync = self.startSync()
|
||||||
elapsedTime = datetime.now() - startTime
|
elapsedTime = datetime.now() - startTime
|
||||||
self.logMsg("SyncDatabase (finished in: %s) %s"
|
log("SyncDatabase (finished in: %s) %s"
|
||||||
% (str(elapsedTime).split('.')[0], librarySync), 1)
|
% (str(elapsedTime).split('.')[0], librarySync), 1)
|
||||||
# Only try the initial sync once per kodi session regardless
|
# Only try the initial sync once per kodi session regardless
|
||||||
# This will prevent an infinite loop in case something goes wrong.
|
# This will prevent an infinite loop in case something goes wrong.
|
||||||
|
@ -999,32 +983,32 @@ class LibrarySync(threading.Thread):
|
||||||
# Set in kodimonitor.py
|
# Set in kodimonitor.py
|
||||||
window('emby_onWake', clear=True)
|
window('emby_onWake', clear=True)
|
||||||
if window('emby_syncRunning') != "true":
|
if window('emby_syncRunning') != "true":
|
||||||
self.logMsg("SyncDatabase onWake (started)", 0)
|
log("SyncDatabase onWake (started)", 0)
|
||||||
librarySync = self.startSync()
|
librarySync = self.startSync()
|
||||||
self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
log("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
||||||
|
|
||||||
if self.stop_thread:
|
if self.stop_thread:
|
||||||
# Set in service.py
|
# Set in service.py
|
||||||
self.logMsg("Service terminated thread.", 2)
|
log("Service terminated thread.", 2)
|
||||||
break
|
break
|
||||||
|
|
||||||
if self.monitor.waitForAbort(1):
|
if self.monitor.waitForAbort(1):
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
break
|
break
|
||||||
|
|
||||||
self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
|
log("###===--- LibrarySync Stopped ---===###", 0)
|
||||||
|
|
||||||
def stopThread(self):
|
def stopThread(self):
|
||||||
self.stop_thread = True
|
self.stop_thread = True
|
||||||
self.logMsg("Ending thread...", 2)
|
log("Ending thread...", 2)
|
||||||
|
|
||||||
def suspendThread(self):
|
def suspendThread(self):
|
||||||
self.suspend_thread = True
|
self.suspend_thread = True
|
||||||
self.logMsg("Pausing thread...", 0)
|
log("Pausing thread...", 0)
|
||||||
|
|
||||||
def resumeThread(self):
|
def resumeThread(self):
|
||||||
self.suspend_thread = False
|
self.suspend_thread = False
|
||||||
self.logMsg("Resuming thread...", 0)
|
log("Resuming thread...", 0)
|
||||||
|
|
||||||
|
|
||||||
class ManualSync(LibrarySync):
|
class ManualSync(LibrarySync):
|
||||||
|
@ -1041,14 +1025,13 @@ class ManualSync(LibrarySync):
|
||||||
|
|
||||||
def movies(self, embycursor, kodicursor, pdialog):
|
def movies(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
lang = utils.language
|
|
||||||
# Get movies from emby
|
# Get movies from emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
movies = itemtypes.Movies(embycursor, kodicursor)
|
movies = itemtypes.Movies(embycursor, kodicursor)
|
||||||
|
|
||||||
views = emby_db.getView_byType('movies')
|
views = emby_db.getView_byType('movies')
|
||||||
views += emby_db.getView_byType('mixed')
|
views += emby_db.getView_byType('mixed')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
# Pull the list of movies and boxsets in Kodi
|
# Pull the list of movies and boxsets in Kodi
|
||||||
try:
|
try:
|
||||||
|
@ -1095,7 +1078,7 @@ class ManualSync(LibrarySync):
|
||||||
# Only update if movie is not in Kodi or checksum is different
|
# Only update if movie is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
|
|
||||||
self.logMsg("Movies to update for %s: %s" % (viewName, updatelist), 1)
|
log("Movies to update for %s: %s" % (viewName, updatelist), 1)
|
||||||
embymovies = self.emby.getFullItems(updatelist)
|
embymovies = self.emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1137,7 +1120,7 @@ class ManualSync(LibrarySync):
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
embyboxsets.append(boxset)
|
embyboxsets.append(boxset)
|
||||||
|
|
||||||
self.logMsg("Boxsets to update: %s" % updatelist, 1)
|
log("Boxsets to update: %s" % updatelist, 1)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
|
|
||||||
if pdialog:
|
if pdialog:
|
||||||
|
@ -1161,13 +1144,13 @@ class ManualSync(LibrarySync):
|
||||||
if kodimovie not in all_embymoviesIds:
|
if kodimovie not in all_embymoviesIds:
|
||||||
movies.remove(kodimovie)
|
movies.remove(kodimovie)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Movies compare finished.", 1)
|
log("Movies compare finished.", 1)
|
||||||
|
|
||||||
for boxset in all_kodisets:
|
for boxset in all_kodisets:
|
||||||
if boxset not in all_embyboxsetsIds:
|
if boxset not in all_embyboxsetsIds:
|
||||||
movies.remove(boxset)
|
movies.remove(boxset)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Boxsets compare finished.", 1)
|
log("Boxsets compare finished.", 1)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -1178,7 +1161,7 @@ class ManualSync(LibrarySync):
|
||||||
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
||||||
|
|
||||||
views = emby_db.getView_byType('musicvideos')
|
views = emby_db.getView_byType('musicvideos')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
# Pull the list of musicvideos in Kodi
|
# Pull the list of musicvideos in Kodi
|
||||||
try:
|
try:
|
||||||
|
@ -1201,7 +1184,7 @@ class ManualSync(LibrarySync):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="%s %s..." % (utils.language(33028), viewName))
|
message="%s %s..." % (lang(33028), viewName))
|
||||||
|
|
||||||
all_embymvideos = self.emby.getMusicVideos(viewId, basic=True, dialog=pdialog)
|
all_embymvideos = self.emby.getMusicVideos(viewId, basic=True, dialog=pdialog)
|
||||||
for embymvideo in all_embymvideos['Items']:
|
for embymvideo in all_embymvideos['Items']:
|
||||||
|
@ -1218,7 +1201,7 @@ class ManualSync(LibrarySync):
|
||||||
# Only update if musicvideo is not in Kodi or checksum is different
|
# Only update if musicvideo is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
|
|
||||||
self.logMsg("MusicVideos to update for %s: %s" % (viewName, updatelist), 1)
|
log("MusicVideos to update for %s: %s" % (viewName, updatelist), 1)
|
||||||
embymvideos = self.emby.getFullItems(updatelist)
|
embymvideos = self.emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1245,20 +1228,19 @@ class ManualSync(LibrarySync):
|
||||||
if kodimvideo not in all_embymvideosIds:
|
if kodimvideo not in all_embymvideosIds:
|
||||||
mvideos.remove(kodimvideo)
|
mvideos.remove(kodimvideo)
|
||||||
else:
|
else:
|
||||||
self.logMsg("MusicVideos compare finished.", 1)
|
log("MusicVideos compare finished.", 1)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def tvshows(self, embycursor, kodicursor, pdialog):
|
def tvshows(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
lang = utils.language
|
|
||||||
# Get shows from emby
|
# Get shows from emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
tvshows = itemtypes.TVShows(embycursor, kodicursor)
|
tvshows = itemtypes.TVShows(embycursor, kodicursor)
|
||||||
|
|
||||||
views = emby_db.getView_byType('tvshows')
|
views = emby_db.getView_byType('tvshows')
|
||||||
views += emby_db.getView_byType('mixed')
|
views += emby_db.getView_byType('mixed')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
# Pull the list of tvshows and episodes in Kodi
|
# Pull the list of tvshows and episodes in Kodi
|
||||||
try:
|
try:
|
||||||
|
@ -1305,7 +1287,7 @@ class ManualSync(LibrarySync):
|
||||||
# Only update if movie is not in Kodi or checksum is different
|
# Only update if movie is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
|
|
||||||
self.logMsg("TVShows to update for %s: %s" % (viewName, updatelist), 1)
|
log("TVShows to update for %s: %s" % (viewName, updatelist), 1)
|
||||||
embytvshows = self.emby.getFullItems(updatelist)
|
embytvshows = self.emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1349,7 +1331,7 @@ class ManualSync(LibrarySync):
|
||||||
# Only update if movie is not in Kodi or checksum is different
|
# Only update if movie is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
|
|
||||||
self.logMsg("Episodes to update for %s: %s" % (viewName, updatelist), 1)
|
log("Episodes to update for %s: %s" % (viewName, updatelist), 1)
|
||||||
embyepisodes = self.emby.getFullItems(updatelist)
|
embyepisodes = self.emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1374,13 +1356,13 @@ class ManualSync(LibrarySync):
|
||||||
if koditvshow not in all_embytvshowsIds:
|
if koditvshow not in all_embytvshowsIds:
|
||||||
tvshows.remove(koditvshow)
|
tvshows.remove(koditvshow)
|
||||||
else:
|
else:
|
||||||
self.logMsg("TVShows compare finished.", 1)
|
log("TVShows compare finished.", 1)
|
||||||
|
|
||||||
for kodiepisode in all_kodiepisodes:
|
for kodiepisode in all_kodiepisodes:
|
||||||
if kodiepisode not in all_embyepisodesIds:
|
if kodiepisode not in all_embyepisodesIds:
|
||||||
tvshows.remove(kodiepisode)
|
tvshows.remove(kodiepisode)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Episodes compare finished.", 1)
|
log("Episodes compare finished.", 1)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -1421,7 +1403,7 @@ class ManualSync(LibrarySync):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="%s %s..." % (utils.language(33031), data_type))
|
message="%s %s..." % (lang(33031), data_type))
|
||||||
if data_type != "artists":
|
if data_type != "artists":
|
||||||
all_embyitems = process[data_type][0](basic=True, dialog=pdialog)
|
all_embyitems = process[data_type][0](basic=True, dialog=pdialog)
|
||||||
else:
|
else:
|
||||||
|
@ -1446,7 +1428,7 @@ class ManualSync(LibrarySync):
|
||||||
if all_kodisongs.get(itemid) != API.getChecksum():
|
if all_kodisongs.get(itemid) != API.getChecksum():
|
||||||
# Only update if songs is not in Kodi or checksum is different
|
# Only update if songs is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
self.logMsg("%s to update: %s" % (data_type, updatelist), 1)
|
log("%s to update: %s" % (data_type, updatelist), 1)
|
||||||
embyitems = self.emby.getFullItems(updatelist)
|
embyitems = self.emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1467,15 +1449,15 @@ class ManualSync(LibrarySync):
|
||||||
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
|
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
|
||||||
music.remove(kodiartist)
|
music.remove(kodiartist)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Artist compare finished.", 1)
|
log("Artist compare finished.", 1)
|
||||||
for kodialbum in all_kodialbums:
|
for kodialbum in all_kodialbums:
|
||||||
if kodialbum not in all_embyalbumsIds:
|
if kodialbum not in all_embyalbumsIds:
|
||||||
music.remove(kodialbum)
|
music.remove(kodialbum)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Albums compare finished.", 1)
|
log("Albums compare finished.", 1)
|
||||||
for kodisong in all_kodisongs:
|
for kodisong in all_kodisongs:
|
||||||
if kodisong not in all_embysongsIds:
|
if kodisong not in all_embysongsIds:
|
||||||
music.remove(kodisong)
|
music.remove(kodisong)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Songs compare finished.", 1)
|
log("Songs compare finished.", 1)
|
||||||
return True
|
return True
|
|
@ -14,20 +14,18 @@ from mutagen import id3
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
import read_embyserver as embyserver
|
import read_embyserver as embyserver
|
||||||
import utils
|
from utils import Logging, window
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
# Helper for the music library, intended to fix missing song ID3 tags on Emby
|
# Helper for the music library, intended to fix missing song ID3 tags on Emby
|
||||||
|
log = Logging('MusicTools').log
|
||||||
def logMsg(msg, lvl=1):
|
|
||||||
utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl)
|
|
||||||
|
|
||||||
def getRealFileName(filename, isTemp=False):
|
def getRealFileName(filename, isTemp=False):
|
||||||
#get the filename path accessible by python if possible...
|
#get the filename path accessible by python if possible...
|
||||||
|
|
||||||
if not xbmcvfs.exists(filename):
|
if not xbmcvfs.exists(filename):
|
||||||
logMsg( "File does not exist! %s" %(filename), 0)
|
log("File does not exist! %s" % filename, 0)
|
||||||
return (False, "")
|
return (False, "")
|
||||||
|
|
||||||
#if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string
|
#if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string
|
||||||
|
@ -104,7 +102,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei
|
||||||
elif file_rating is None and not currentvalue:
|
elif file_rating is None and not currentvalue:
|
||||||
return (emby_rating, comment, False)
|
return (emby_rating, comment, False)
|
||||||
|
|
||||||
logMsg("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
|
log("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
|
||||||
|
|
||||||
updateFileRating = False
|
updateFileRating = False
|
||||||
updateEmbyRating = False
|
updateEmbyRating = False
|
||||||
|
@ -171,7 +169,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei
|
||||||
if updateEmbyRating and enableexportsongrating:
|
if updateEmbyRating and enableexportsongrating:
|
||||||
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
|
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
|
||||||
like, favourite, deletelike = getEmbyRatingFromKodiRating(rating)
|
like, favourite, deletelike = getEmbyRatingFromKodiRating(rating)
|
||||||
utils.window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update
|
window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update
|
||||||
emby.updateUserRating(embyid, like, favourite, deletelike)
|
emby.updateUserRating(embyid, like, favourite, deletelike)
|
||||||
|
|
||||||
return (rating, comment, hasEmbeddedCover)
|
return (rating, comment, hasEmbeddedCover)
|
||||||
|
@ -183,7 +181,7 @@ def getSongTags(file):
|
||||||
hasEmbeddedCover = False
|
hasEmbeddedCover = False
|
||||||
|
|
||||||
isTemp,filename = getRealFileName(file)
|
isTemp,filename = getRealFileName(file)
|
||||||
logMsg( "getting song ID3 tags for " + filename)
|
log( "getting song ID3 tags for " + filename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
###### FLAC FILES #############
|
###### FLAC FILES #############
|
||||||
|
@ -217,14 +215,14 @@ def getSongTags(file):
|
||||||
#POPM rating is 0-255 and needs to be converted to 0-5 range
|
#POPM rating is 0-255 and needs to be converted to 0-5 range
|
||||||
if rating > 5: rating = (rating / 255) * 5
|
if rating > 5: rating = (rating / 255) * 5
|
||||||
else:
|
else:
|
||||||
logMsg( "Not supported fileformat or unable to access file: %s" %(filename))
|
log( "Not supported fileformat or unable to access file: %s" %(filename))
|
||||||
|
|
||||||
#the rating must be a round value
|
#the rating must be a round value
|
||||||
rating = int(round(rating,0))
|
rating = int(round(rating,0))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
#file in use ?
|
#file in use ?
|
||||||
utils.logMsg("Exception in getSongTags", str(e),0)
|
log("Exception in getSongTags", str(e),0)
|
||||||
rating = None
|
rating = None
|
||||||
|
|
||||||
#remove tempfile if needed....
|
#remove tempfile if needed....
|
||||||
|
@ -246,7 +244,7 @@ def updateRatingToFile(rating, file):
|
||||||
xbmcvfs.copy(file, tempfile)
|
xbmcvfs.copy(file, tempfile)
|
||||||
tempfile = xbmc.translatePath(tempfile).decode("utf-8")
|
tempfile = xbmc.translatePath(tempfile).decode("utf-8")
|
||||||
|
|
||||||
logMsg( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile))
|
log( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile))
|
||||||
|
|
||||||
if not tempfile:
|
if not tempfile:
|
||||||
return
|
return
|
||||||
|
@ -263,7 +261,7 @@ def updateRatingToFile(rating, file):
|
||||||
audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
|
audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
|
||||||
audio.save()
|
audio.save()
|
||||||
else:
|
else:
|
||||||
logMsg( "Not supported fileformat: %s" %(tempfile))
|
log( "Not supported fileformat: %s" %(tempfile))
|
||||||
|
|
||||||
#once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp
|
#once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp
|
||||||
#safety check: we check the file size of the temp file before proceeding with overwite of original file
|
#safety check: we check the file size of the temp file before proceeding with overwite of original file
|
||||||
|
@ -274,14 +272,14 @@ def updateRatingToFile(rating, file):
|
||||||
xbmcvfs.delete(file)
|
xbmcvfs.delete(file)
|
||||||
xbmcvfs.copy(tempfile,file)
|
xbmcvfs.copy(tempfile,file)
|
||||||
else:
|
else:
|
||||||
logMsg( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile))
|
log( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile))
|
||||||
|
|
||||||
#always delete the tempfile
|
#always delete the tempfile
|
||||||
xbmcvfs.delete(tempfile)
|
xbmcvfs.delete(tempfile)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
#file in use ?
|
#file in use ?
|
||||||
logMsg("Exception in updateRatingToFile %s" %e,0)
|
log("Exception in updateRatingToFile %s" %e,0)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ import json
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
import utils
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import downloadutils
|
import downloadutils
|
||||||
import kodidb_functions as kodidb
|
import kodidb_functions as kodidb
|
||||||
import websocket_client as wsc
|
import websocket_client as wsc
|
||||||
|
from utils import Logging, window, settings, language as lang
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -28,6 +28,9 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
|
@ -36,20 +39,13 @@ class Player(xbmc.Player):
|
||||||
self.ws = wsc.WebSocket_Client()
|
self.ws = wsc.WebSocket_Client()
|
||||||
self.xbmcplayer = xbmc.Player()
|
self.xbmcplayer = xbmc.Player()
|
||||||
|
|
||||||
self.logMsg("Starting playback monitor.", 2)
|
log("Starting playback monitor.", 2)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
self.className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def GetPlayStats(self):
|
def GetPlayStats(self):
|
||||||
return self.playStats
|
return self.playStats
|
||||||
|
|
||||||
def onPlayBackStarted(self):
|
def onPlayBackStarted(self):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
# Will be called when xbmc starts playing a file
|
# Will be called when xbmc starts playing a file
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
|
@ -67,7 +63,7 @@ class Player(xbmc.Player):
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
if count == 5: # try 5 times
|
if count == 5: # try 5 times
|
||||||
self.logMsg("Cancelling playback report...", 1)
|
log("Cancelling playback report...", 1)
|
||||||
break
|
break
|
||||||
else: count += 1
|
else: count += 1
|
||||||
|
|
||||||
|
@ -84,12 +80,12 @@ class Player(xbmc.Player):
|
||||||
xbmc.sleep(200)
|
xbmc.sleep(200)
|
||||||
itemId = window("emby_%s.itemid" % currentFile)
|
itemId = window("emby_%s.itemid" % currentFile)
|
||||||
if tryCount == 20: # try 20 times or about 10 seconds
|
if tryCount == 20: # try 20 times or about 10 seconds
|
||||||
self.logMsg("Could not find itemId, cancelling playback report...", 1)
|
log("Could not find itemId, cancelling playback report...", 1)
|
||||||
break
|
break
|
||||||
else: tryCount += 1
|
else: tryCount += 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
log("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
||||||
|
|
||||||
# Only proceed if an itemId was found.
|
# Only proceed if an itemId was found.
|
||||||
embyitem = "emby_%s" % currentFile
|
embyitem = "emby_%s" % currentFile
|
||||||
|
@ -102,7 +98,7 @@ class Player(xbmc.Player):
|
||||||
customseek = window('emby_customPlaylist.seektime')
|
customseek = window('emby_customPlaylist.seektime')
|
||||||
if window('emby_customPlaylist') == "true" and customseek:
|
if window('emby_customPlaylist') == "true" and customseek:
|
||||||
# Start at, when using custom playlist (play to Kodi from webclient)
|
# Start at, when using custom playlist (play to Kodi from webclient)
|
||||||
self.logMsg("Seeking to: %s" % customseek, 1)
|
log("Seeking to: %s" % customseek, 1)
|
||||||
self.xbmcplayer.seekTime(int(customseek)/10000000.0)
|
self.xbmcplayer.seekTime(int(customseek)/10000000.0)
|
||||||
window('emby_customPlaylist.seektime', clear=True)
|
window('emby_customPlaylist.seektime', clear=True)
|
||||||
|
|
||||||
|
@ -189,7 +185,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
if mapping: # Set in playbackutils.py
|
if mapping: # Set in playbackutils.py
|
||||||
|
|
||||||
self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
|
log("Mapping for external subtitles index: %s" % mapping, 2)
|
||||||
externalIndex = json.loads(mapping)
|
externalIndex = json.loads(mapping)
|
||||||
|
|
||||||
if externalIndex.get(str(indexSubs)):
|
if externalIndex.get(str(indexSubs)):
|
||||||
|
@ -207,7 +203,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
|
|
||||||
# Post playback to server
|
# Post playback to server
|
||||||
self.logMsg("Sending POST play started: %s." % postdata, 2)
|
log("Sending POST play started: %s." % postdata, 2)
|
||||||
self.doUtils(url, postBody=postdata, action_type="POST")
|
self.doUtils(url, postBody=postdata, action_type="POST")
|
||||||
|
|
||||||
# Ensure we do have a runtime
|
# Ensure we do have a runtime
|
||||||
|
@ -215,7 +211,7 @@ class Player(xbmc.Player):
|
||||||
runtime = int(runtime)
|
runtime = int(runtime)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
runtime = self.xbmcplayer.getTotalTime()
|
runtime = self.xbmcplayer.getTotalTime()
|
||||||
self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
log("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||||
|
|
||||||
# Save data map for updates and position calls
|
# Save data map for updates and position calls
|
||||||
data = {
|
data = {
|
||||||
|
@ -232,7 +228,7 @@ class Player(xbmc.Player):
|
||||||
}
|
}
|
||||||
|
|
||||||
self.played_info[currentFile] = data
|
self.played_info[currentFile] = data
|
||||||
self.logMsg("ADDING_FILE: %s" % self.played_info, 1)
|
log("ADDING_FILE: %s" % self.played_info, 1)
|
||||||
|
|
||||||
# log some playback stats
|
# log some playback stats
|
||||||
'''if(itemType != None):
|
'''if(itemType != None):
|
||||||
|
@ -251,7 +247,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
def reportPlayback(self):
|
def reportPlayback(self):
|
||||||
|
|
||||||
self.logMsg("reportPlayback Called", 2)
|
log("reportPlayback Called", 2)
|
||||||
|
|
||||||
# Get current file
|
# Get current file
|
||||||
currentFile = self.currentFile
|
currentFile = self.currentFile
|
||||||
|
@ -345,11 +341,11 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
# Number of audiotracks to help get Emby Index
|
# Number of audiotracks to help get Emby Index
|
||||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||||
mapping = utils.window("emby_%s.indexMapping" % currentFile)
|
mapping = window("emby_%s.indexMapping" % currentFile)
|
||||||
|
|
||||||
if mapping: # Set in PlaybackUtils.py
|
if mapping: # Set in PlaybackUtils.py
|
||||||
|
|
||||||
self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
|
log("Mapping for external subtitles index: %s" % mapping, 2)
|
||||||
externalIndex = json.loads(mapping)
|
externalIndex = json.loads(mapping)
|
||||||
|
|
||||||
if externalIndex.get(str(indexSubs)):
|
if externalIndex.get(str(indexSubs)):
|
||||||
|
@ -369,13 +365,13 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
# Report progress via websocketclient
|
# Report progress via websocketclient
|
||||||
postdata = json.dumps(postdata)
|
postdata = json.dumps(postdata)
|
||||||
self.logMsg("Report: %s" % postdata, 2)
|
log("Report: %s" % postdata, 2)
|
||||||
self.ws.sendProgressUpdate(postdata)
|
self.ws.sendProgressUpdate(postdata)
|
||||||
|
|
||||||
def onPlayBackPaused(self):
|
def onPlayBackPaused(self):
|
||||||
|
|
||||||
currentFile = self.currentFile
|
currentFile = self.currentFile
|
||||||
self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
|
log("PLAYBACK_PAUSED: %s" % currentFile, 2)
|
||||||
|
|
||||||
if self.played_info.get(currentFile):
|
if self.played_info.get(currentFile):
|
||||||
self.played_info[currentFile]['paused'] = True
|
self.played_info[currentFile]['paused'] = True
|
||||||
|
@ -385,7 +381,7 @@ class Player(xbmc.Player):
|
||||||
def onPlayBackResumed(self):
|
def onPlayBackResumed(self):
|
||||||
|
|
||||||
currentFile = self.currentFile
|
currentFile = self.currentFile
|
||||||
self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
|
log("PLAYBACK_RESUMED: %s" % currentFile, 2)
|
||||||
|
|
||||||
if self.played_info.get(currentFile):
|
if self.played_info.get(currentFile):
|
||||||
self.played_info[currentFile]['paused'] = False
|
self.played_info[currentFile]['paused'] = False
|
||||||
|
@ -395,7 +391,7 @@ class Player(xbmc.Player):
|
||||||
def onPlayBackSeek(self, time, seekOffset):
|
def onPlayBackSeek(self, time, seekOffset):
|
||||||
# Make position when seeking a bit more accurate
|
# Make position when seeking a bit more accurate
|
||||||
currentFile = self.currentFile
|
currentFile = self.currentFile
|
||||||
self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
|
log("PLAYBACK_SEEK: %s" % currentFile, 2)
|
||||||
|
|
||||||
if self.played_info.get(currentFile):
|
if self.played_info.get(currentFile):
|
||||||
position = self.xbmcplayer.getTime()
|
position = self.xbmcplayer.getTime()
|
||||||
|
@ -404,39 +400,34 @@ class Player(xbmc.Player):
|
||||||
self.reportPlayback()
|
self.reportPlayback()
|
||||||
|
|
||||||
def onPlayBackStopped(self):
|
def onPlayBackStopped(self):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
# Will be called when user stops xbmc playing a file
|
# Will be called when user stops xbmc playing a file
|
||||||
self.logMsg("ONPLAYBACK_STOPPED", 2)
|
log("ONPLAYBACK_STOPPED", 2)
|
||||||
window('emby_customPlaylist', clear=True)
|
window('emby_customPlaylist', clear=True)
|
||||||
window('emby_customPlaylist.seektime', clear=True)
|
window('emby_customPlaylist.seektime', clear=True)
|
||||||
window('emby_playbackProps', clear=True)
|
window('emby_playbackProps', clear=True)
|
||||||
self.logMsg("Clear playlist properties.", 1)
|
log("Clear playlist properties.", 1)
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
def onPlayBackEnded(self):
|
def onPlayBackEnded(self):
|
||||||
# Will be called when xbmc stops playing a file
|
# Will be called when xbmc stops playing a file
|
||||||
self.logMsg("ONPLAYBACK_ENDED", 2)
|
log("ONPLAYBACK_ENDED", 2)
|
||||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
window('emby_customPlaylist.seektime', clear=True)
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
def stopAll(self):
|
def stopAll(self):
|
||||||
|
|
||||||
lang = utils.language
|
|
||||||
settings = utils.settings
|
|
||||||
|
|
||||||
if not self.played_info:
|
if not self.played_info:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.logMsg("Played_information: %s" % self.played_info, 1)
|
log("Played_information: %s" % self.played_info, 1)
|
||||||
# Process each items
|
# Process each items
|
||||||
for item in self.played_info:
|
for item in self.played_info:
|
||||||
|
|
||||||
data = self.played_info.get(item)
|
data = self.played_info.get(item)
|
||||||
if data:
|
if data:
|
||||||
|
|
||||||
self.logMsg("Item path: %s" % item, 2)
|
log("Item path: %s" % item, 2)
|
||||||
self.logMsg("Item data: %s" % data, 2)
|
log("Item data: %s" % data, 2)
|
||||||
|
|
||||||
runtime = data['runtime']
|
runtime = data['runtime']
|
||||||
currentPosition = data['currentPosition']
|
currentPosition = data['currentPosition']
|
||||||
|
@ -447,7 +438,7 @@ class Player(xbmc.Player):
|
||||||
playMethod = data['playmethod']
|
playMethod = data['playmethod']
|
||||||
|
|
||||||
# Prevent manually mark as watched in Kodi monitor
|
# Prevent manually mark as watched in Kodi monitor
|
||||||
utils.window('emby_skipWatched%s' % itemid, value="true")
|
window('emby_skipWatched%s' % itemid, value="true")
|
||||||
|
|
||||||
if currentPosition and runtime:
|
if currentPosition and runtime:
|
||||||
try:
|
try:
|
||||||
|
@ -457,7 +448,7 @@ class Player(xbmc.Player):
|
||||||
percentComplete = 0
|
percentComplete = 0
|
||||||
|
|
||||||
markPlayedAt = float(settings('markPlayed')) / 100
|
markPlayedAt = float(settings('markPlayed')) / 100
|
||||||
self.logMsg("Percent complete: %s Mark played at: %s"
|
log("Percent complete: %s Mark played at: %s"
|
||||||
% (percentComplete, markPlayedAt), 1)
|
% (percentComplete, markPlayedAt), 1)
|
||||||
|
|
||||||
# Send the delete action to the server.
|
# Send the delete action to the server.
|
||||||
|
@ -475,18 +466,18 @@ class Player(xbmc.Player):
|
||||||
if percentComplete >= markPlayedAt and offerDelete:
|
if percentComplete >= markPlayedAt and offerDelete:
|
||||||
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000)
|
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000)
|
||||||
if not resp:
|
if not resp:
|
||||||
self.logMsg("User skipped deletion.", 1)
|
log("User skipped deletion.", 1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||||
self.logMsg("Deleting request: %s" % itemid, 1)
|
log("Deleting request: %s" % itemid, 1)
|
||||||
self.doUtils(url, action_type="DELETE")
|
self.doUtils(url, action_type="DELETE")
|
||||||
|
|
||||||
self.stopPlayback(data)
|
self.stopPlayback(data)
|
||||||
|
|
||||||
# Stop transcoding
|
# Stop transcoding
|
||||||
if playMethod == "Transcode":
|
if playMethod == "Transcode":
|
||||||
self.logMsg("Transcoding for %s terminated." % itemid, 1)
|
log("Transcoding for %s terminated." % itemid, 1)
|
||||||
deviceId = self.clientInfo.getDeviceId()
|
deviceId = self.clientInfo.getDeviceId()
|
||||||
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
||||||
self.doUtils(url, action_type="DELETE")
|
self.doUtils(url, action_type="DELETE")
|
||||||
|
@ -495,7 +486,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
def stopPlayback(self, data):
|
def stopPlayback(self, data):
|
||||||
|
|
||||||
self.logMsg("stopPlayback called", 2)
|
log("stopPlayback called", 2)
|
||||||
|
|
||||||
itemId = data['item_id']
|
itemId = data['item_id']
|
||||||
currentPosition = data['currentPosition']
|
currentPosition = data['currentPosition']
|
||||||
|
|
|
@ -188,7 +188,7 @@ def setScreensaver(value):
|
||||||
result = xbmc.executeJSONRPC(json.dumps(query))
|
result = xbmc.executeJSONRPC(json.dumps(query))
|
||||||
log("Toggling screensaver: %s %s" % (value, result), 1)
|
log("Toggling screensaver: %s %s" % (value, result), 1)
|
||||||
|
|
||||||
def convertdate(date):
|
def convertDate(date):
|
||||||
try:
|
try:
|
||||||
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
|
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
|
Loading…
Reference in a new issue