Centralized logging

This commit is contained in:
angelblue05 2016-06-17 22:03:28 -05:00
parent 02e7c2946b
commit 5658801f72
10 changed files with 336 additions and 373 deletions

View file

@ -6,8 +6,8 @@ import json
import requests
import logging
import utils
import clientinfo
from utils import Logging, window
##################################################################################################
@ -34,28 +34,26 @@ class ConnectUtils():
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
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):
# Reserved for userclient only
self.userId = userId
self.logMsg("Set connect userId: %s" % userId, 2)
log("Set connect userId: %s" % userId, 2)
def setServer(self, server):
# Reserved for userclient only
self.server = server
self.logMsg("Set connect server: %s" % server, 2)
log("Set connect server: %s" % server, 2)
def setToken(self, token):
# Reserved for userclient only
self.token = token
self.logMsg("Set connect token: %s" % token, 2)
log("Set connect token: %s" % token, 2)
def startSession(self):
@ -73,7 +71,7 @@ class ConnectUtils():
if self.sslclient is not None:
verify = self.sslclient
except:
self.logMsg("Could not load SSL settings.", 1)
log("Could not load SSL settings.", 1)
# Start session
self.c = requests.Session()
@ -83,13 +81,13 @@ class ConnectUtils():
self.c.mount("http://", 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):
try:
self.c.close()
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):
@ -103,7 +101,7 @@ class ConnectUtils():
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': "application/json"
}
self.logMsg("Header: %s" % header, 1)
log("Header: %s" % header, 1)
else:
token = self.token
@ -115,17 +113,17 @@ class ConnectUtils():
'X-Application': "Kodi/%s" % version,
'X-Connect-UserToken': token
}
self.logMsg("Header: %s" % header, 1)
log("Header: %s" % header, 1)
return header
def doUrl(self, url, data=None, postBody=None, rtype="GET",
parameters=None, authenticate=True, timeout=None):
window = utils.window
self.logMsg("=== ENTER connectUrl ===", 2)
log("=== ENTER connectUrl ===", 2)
default_link = ""
if timeout is None:
timeout = self.timeout
@ -209,25 +207,25 @@ class ConnectUtils():
verify=verifyssl)
##### THE RESPONSE #####
self.logMsg(r.url, 1)
self.logMsg(r, 1)
log(r.url, 1)
log(r, 1)
if r.status_code == 204:
# No body in the response
self.logMsg("====== 204 Success ======", 1)
log("====== 204 Success ======", 1)
elif r.status_code == requests.codes.ok:
try:
# UNICODE - JSON object
r = r.json()
self.logMsg("====== 200 Success ======", 1)
self.logMsg("Response: %s" % r, 1)
log("====== 200 Success ======", 1)
log("Response: %s" % r, 1)
return r
except:
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:
r.raise_for_status()
@ -238,8 +236,8 @@ class ConnectUtils():
pass
except requests.exceptions.ConnectTimeout as e:
self.logMsg("Server timeout at: %s" % url, 0)
self.logMsg(e, 1)
log("Server timeout at: %s" % url, 0)
log(e, 1)
except requests.exceptions.HTTPError as e:
@ -255,11 +253,11 @@ class ConnectUtils():
pass
except requests.exceptions.SSLError as e:
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
self.logMsg(e, 1)
log("Invalid SSL certificate for: %s" % url, 0)
log(e, 1)
except requests.exceptions.RequestException as e:
self.logMsg("Unknown error connecting to: %s" % url, 0)
self.logMsg(e, 1)
log("Unknown error connecting to: %s" % url, 0)
log(e, 1)
return default_link
return default_link

View file

@ -9,14 +9,15 @@ import logging
import xbmc
import xbmcgui
import utils
import clientinfo
from utils import Logging, window, settings
##################################################################################################
# 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(InsecurePlatformWarning)
#logging.getLogger('requests').setLevel(logging.WARNING)
##################################################################################################
@ -36,40 +37,38 @@ class DownloadUtils():
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
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):
# Reserved for userclient only
self.username = username
self.logMsg("Set username: %s" % username, 2)
log("Set username: %s" % username, 2)
def setUserId(self, userId):
# Reserved for userclient only
self.userId = userId
self.logMsg("Set userId: %s" % userId, 2)
log("Set userId: %s" % userId, 2)
def setServer(self, server):
# Reserved for userclient only
self.server = server
self.logMsg("Set server: %s" % server, 2)
log("Set server: %s" % server, 2)
def setToken(self, token):
# Reserved for userclient only
self.token = token
self.logMsg("Set token: %s" % token, 2)
log("Set token: %s" % token, 2)
def setSSL(self, ssl, sslclient):
# Reserved for userclient only
self.sslverify = ssl
self.sslclient = sslclient
self.logMsg("Verify SSL host certificate: %s" % ssl, 2)
self.logMsg("SSL client side certificate: %s" % sslclient, 2)
log("Verify SSL host certificate: %s" % ssl, 2)
log("SSL client side certificate: %s" % sslclient, 2)
def postCapabilities(self, deviceId):
@ -94,11 +93,11 @@ class DownloadUtils():
)
}
self.logMsg("Capabilities URL: %s" % url, 2)
self.logMsg("Postdata: %s" % data, 2)
log("Capabilities URL: %s" % url, 2)
log("Postdata: %s" % data, 2)
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
url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
@ -107,20 +106,19 @@ class DownloadUtils():
sessionId = result[0]['Id']
except (KeyError, TypeError):
self.logMsg("Failed to retrieve sessionId.", 1)
log("Failed to retrieve sessionId.", 1)
else:
self.logMsg("Session: %s" % result, 2)
self.logMsg("SessionId: %s" % sessionId, 1)
utils.window('emby_sessionId', value=sessionId)
log("Session: %s" % result, 2)
log("SessionId: %s" % sessionId, 1)
window('emby_sessionId', value=sessionId)
# Post any permanent additional users
additionalUsers = utils.settings('additionalUsers')
additionalUsers = settings('additionalUsers')
if additionalUsers:
additionalUsers = additionalUsers.split(',')
self.logMsg(
"List of permanent users added to the session: %s"
log("List of permanent users added to the session: %s"
% additionalUsers, 1)
# Get the user list from server to get the userId
@ -158,7 +156,7 @@ class DownloadUtils():
if self.sslclient is not None:
verify = self.sslclient
except:
self.logMsg("Could not load SSL settings.", 1)
log("Could not load SSL settings.", 1)
# Start session
self.s = requests.Session()
@ -168,18 +166,18 @@ class DownloadUtils():
self.s.mount("http://", 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):
try:
self.s.close()
except:
self.logMsg("Requests session could not be terminated.", 1)
log("Requests session could not be terminated.", 1)
def getHeader(self, authenticate=True):
deviceName = self.clientInfo.getDeviceName()
deviceName = utils.normalize_string(deviceName.encode('utf-8'))
deviceName = deviceName.encode('utf-8')
deviceId = self.clientInfo.getDeviceId()
version = self.clientInfo.getVersion()
@ -195,7 +193,7 @@ class DownloadUtils():
'Accept-Charset': 'UTF-8,*',
'Authorization': auth
}
self.logMsg("Header: %s" % header, 2)
log("Header: %s" % header, 2)
else:
userId = self.userId
@ -212,19 +210,20 @@ class DownloadUtils():
'Authorization': auth,
'X-MediaBrowser-Token': token
}
self.logMsg("Header: %s" % header, 2)
log("Header: %s" % header, 2)
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 = ""
try:
# If user is authenticated
if (authenticate):
if authenticate:
# Get requests session
try:
s = self.s
@ -243,18 +242,18 @@ class DownloadUtils():
except AttributeError:
# request session does not exists
# Get user information
self.userId = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userId)
self.token = utils.window('emby_accessToken%s' % self.userId)
self.userId = window('emby_currUser')
self.server = window('emby_server%s' % self.userId)
self.token = window('emby_accessToken%s' % self.userId)
header = self.getHeader()
verifyssl = False
cert = None
# IF user enables ssl verification
if utils.settings('sslverify') == "true":
if settings('sslverify') == "true":
verifyssl = True
if utils.settings('sslcert') != "None":
verifyssl = utils.settings('sslcert')
if settings('sslcert') != "None":
verifyssl = settings('sslcert')
# Replace for the real values
url = url.replace("{server}", self.server)
@ -314,23 +313,23 @@ class DownloadUtils():
verify=verifyssl)
##### THE RESPONSE #####
self.logMsg(r.url, 2)
log(r.url, 2)
if r.status_code == 204:
# No body in the response
self.logMsg("====== 204 Success ======", 2)
log("====== 204 Success ======", 2)
elif r.status_code == requests.codes.ok:
try:
# UNICODE - JSON object
r = r.json()
self.logMsg("====== 200 Success ======", 2)
self.logMsg("Response: %s" % r, 2)
log("====== 200 Success ======", 2)
log("Response: %s" % r, 2)
return r
except:
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:
r.raise_for_status()
@ -338,26 +337,26 @@ class DownloadUtils():
except requests.exceptions.ConnectionError as e:
# Make the addon aware of status
if utils.window('emby_online') != "false":
self.logMsg("Server unreachable at: %s" % url, 0)
self.logMsg(e, 2)
utils.window('emby_online', value="false")
if window('emby_online') != "false":
log("Server unreachable at: %s" % url, 0)
log(e, 2)
window('emby_online', value="false")
except requests.exceptions.ConnectTimeout as e:
self.logMsg("Server timeout at: %s" % url, 0)
self.logMsg(e, 1)
log("Server timeout at: %s" % url, 0)
log(e, 1)
except requests.exceptions.HTTPError as e:
if r.status_code == 401:
# Unauthorized
status = utils.window('emby_serverStatus')
status = window('emby_serverStatus')
if 'X-Application-Error-Code' in r.headers:
# Emby server errors
if r.headers['X-Application-Error-Code'] == "ParentalControl":
# Parental control - access restricted
utils.window('emby_serverStatus', value="restricted")
window('emby_serverStatus', value="restricted")
xbmcgui.Dialog().notification(
heading="Emby server",
message="Access restricted.",
@ -371,8 +370,8 @@ class DownloadUtils():
elif status not in ("401", "Auth"):
# Tell userclient token has been revoked.
utils.window('emby_serverStatus', value="401")
self.logMsg("HTTP Error: %s" % e, 0)
window('emby_serverStatus', value="401")
log("HTTP Error: %s" % e, 0)
xbmcgui.Dialog().notification(
heading="Error connecting",
message="Unauthorized.",
@ -387,11 +386,11 @@ class DownloadUtils():
pass
except requests.exceptions.SSLError as e:
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
self.logMsg(e, 1)
log("Invalid SSL certificate for: %s" % url, 0)
log(e, 1)
except requests.exceptions.RequestException as e:
self.logMsg("Unknown error connecting to: %s" % url, 0)
self.logMsg(e, 1)
log("Unknown error connecting to: %s" % url, 0)
log(e, 1)
return default_link

View file

@ -1,8 +1,14 @@
# -*- coding: utf-8 -*-
#################################################################################################
import threading
import utils
import xbmc
import requests
from utils import Logging
#################################################################################################
class image_cache_thread(threading.Thread):
urlToProcess = None
@ -13,28 +19,32 @@ class image_cache_thread(threading.Thread):
xbmc_username = ""
xbmc_password = ""
def __init__(self):
self.monitor = xbmc.Monitor()
global log
log = Logging(self.__class__.__name__).log
threading.Thread.__init__(self)
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s" % className, msg, lvl)
def setUrl(self, url):
self.urlToProcess = url
def setHost(self, host, port):
self.xbmc_host = host
self.xbmc_port = port
def setAuth(self, user, pwd):
self.xbmc_username = user
self.xbmc_password = pwd
def run(self):
self.logMsg("Image Caching Thread Processing : " + self.urlToProcess, 2)
log("Image Caching Thread Processing: %s" % self.urlToProcess, 2)
try:
response = requests.head(
@ -46,7 +56,5 @@ class image_cache_thread(threading.Thread):
# We don't need the result
except: pass
self.logMsg("Image Caching Thread Exited", 2)
self.isFinished = True
log("Image Caching Thread Exited", 2)
self.isFinished = True

View file

@ -9,10 +9,10 @@ import xbmc
import xbmcgui
import xbmcaddon
import utils
import clientinfo
import downloadutils
import userclient
from utils import Logging, settings, language as lang, passwordsXML
#################################################################################################
@ -22,74 +22,67 @@ class InitialSetup():
def __init__(self):
self.addon = xbmcaddon.Addon()
self.__language__ = self.addon.getLocalizedString
global log
log = Logging(self.__class__.__name__).log
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
self.addonId = self.clientInfo.getAddonId()
self.doUtils = downloadutils.DownloadUtils()
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):
# Check server, user, direct paths, music, direct stream if not direct path.
string = self.__language__
addonId = self.addonId
##### SERVER INFO #####
self.logMsg("Initial setup called.", 2)
log("Initial setup called.", 2)
server = self.userClient.getServer()
if server:
self.logMsg("Server is already set.", 2)
log("Server is already set.", 2)
return
self.logMsg("Looking for server...", 2)
log("Looking for server...", 2)
server = self.getServerDetails()
self.logMsg("Found: %s" % server, 2)
log("Found: %s" % server, 2)
try:
prefix, ip, port = server.replace("/", "").split(":")
except: # Failed to retrieve server information
self.logMsg("getServerDetails failed.", 1)
log("getServerDetails failed.", 1)
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
return
else:
server_confirm = xbmcgui.Dialog().yesno(
heading="Emby for Kodi",
line1="Proceed with the following server?",
line2="%s %s" % (string(30169), server))
line2="%s %s" % (lang(30169), server))
if server_confirm:
# Correct server found
self.logMsg("Server is selected. Saving the information.", 1)
utils.settings('ipaddress', value=ip)
utils.settings('port', value=port)
log("Server is selected. Saving the information.", 1)
settings('ipaddress', value=ip)
settings('port', value=port)
if prefix == "https":
utils.settings('https', value="true")
settings('https', value="true")
else:
# User selected no or cancelled the dialog
self.logMsg("No server selected.", 1)
log("No server selected.", 1)
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
return
##### USER INFO #####
self.logMsg("Getting user list.", 1)
log("Getting user list.", 1)
url = "%s/emby/Users/Public?format=json" % server
result = self.doUtils.downloadUrl(url, authenticate=False)
if result == "":
self.logMsg("Unable to connect to %s" % server, 1)
log("Unable to connect to %s" % server, 1)
return
self.logMsg("Response: %s" % result, 2)
log("Response: %s" % result, 2)
# Process the list of users
usernames = []
users_hasPassword = []
@ -103,14 +96,14 @@ class InitialSetup():
name = "%s (secure)" % name
users_hasPassword.append(name)
self.logMsg("Presenting user list: %s" % users_hasPassword, 1)
user_select = xbmcgui.Dialog().select(string(30200), users_hasPassword)
log("Presenting user list: %s" % users_hasPassword, 1)
user_select = xbmcgui.Dialog().select(lang(30200), users_hasPassword)
if user_select > -1:
selected_user = usernames[user_select]
self.logMsg("Selected user: %s" % selected_user, 1)
utils.settings('username', value=selected_user)
log("Selected user: %s" % selected_user, 1)
settings('username', value=selected_user)
else:
self.logMsg("No user selected.", 1)
log("No user selected.", 1)
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
##### ADDITIONAL PROMPTS #####
@ -126,8 +119,8 @@ class InitialSetup():
nolabel="Addon (Default)",
yeslabel="Native (Direct Paths)")
if directPaths:
self.logMsg("User opted to use direct paths.", 1)
utils.settings('useDirectPaths', value="1")
log("User opted to use direct paths.", 1)
settings('useDirectPaths', value="1")
# ask for credentials
credentials = dialog.yesno(
@ -138,15 +131,15 @@ class InitialSetup():
"during the initial scan of your content if Kodi can't "
"locate your content."))
if credentials:
self.logMsg("Presenting network credentials dialog.", 1)
utils.passwordsXML()
log("Presenting network credentials dialog.", 1)
passwordsXML()
musicDisabled = dialog.yesno(
heading="Music Library",
line1="Disable Emby music library?")
if musicDisabled:
self.logMsg("User opted to disable Emby music library.", 1)
utils.settings('enableMusic', value="false")
log("User opted to disable Emby music library.", 1)
settings('enableMusic', value="false")
else:
# Only prompt if the user didn't select direct paths for videos
if not directPaths:
@ -157,12 +150,12 @@ class InitialSetup():
"this option only if you plan on listening "
"to music outside of your network."))
if musicAccess:
self.logMsg("User opted to direct stream music.", 1)
utils.settings('streamMusic', value="true")
log("User opted to direct stream music.", 1)
settings('streamMusic', value="true")
def getServerDetails(self):
self.logMsg("Getting Server Details from Network", 1)
log("Getting Server Details from Network", 1)
MULTI_GROUP = ("<broadcast>", 7359)
MESSAGE = "who is EmbyServer?"
@ -176,15 +169,15 @@ class InitialSetup():
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)
self.logMsg("MultiGroup : %s" % str(MULTI_GROUP), 2)
self.logMsg("Sending UDP Data: %s" % MESSAGE, 2)
log("MultiGroup : %s" % str(MULTI_GROUP), 2)
log("Sending UDP Data: %s" % MESSAGE, 2)
sock.sendto(MESSAGE, MULTI_GROUP)
try:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
self.logMsg("Received Response: %s" % data)
log("Received Response: %s" % data)
except:
self.logMsg("No UDP Response")
log("No UDP Response")
return None
else:
# Get the address

View file

@ -7,7 +7,7 @@ import xbmc
import api
import artwork
import clientinfo
import utils
from utils import Logging
##################################################################################################
@ -19,16 +19,14 @@ class Kodidb_Functions():
def __init__(self, cursor):
global log
log = Logging(self.__class__.__name__).log
self.cursor = cursor
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
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):
@ -153,7 +151,7 @@ class Kodidb_Functions():
query = "INSERT INTO country(country_id, name) values(?, ?)"
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
query = (
@ -187,7 +185,7 @@ class Kodidb_Functions():
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
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:
# Only movies have a country field
@ -232,7 +230,7 @@ class Kodidb_Functions():
query = "INSERT INTO actor(actor_id, name) values(?, ?)"
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:
# Link person to content
@ -302,7 +300,7 @@ class Kodidb_Functions():
query = "INSERT INTO actors(idActor, strActor) values(?, ?)"
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:
# Link person to content
@ -462,7 +460,7 @@ class Kodidb_Functions():
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
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:
# Assign genre to item
@ -507,7 +505,7 @@ class Kodidb_Functions():
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
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:
# Assign genre to item
@ -566,7 +564,7 @@ class Kodidb_Functions():
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
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
query = (
@ -597,7 +595,7 @@ class Kodidb_Functions():
query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)"
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
if "movie" in mediatype:
@ -728,7 +726,7 @@ class Kodidb_Functions():
self.cursor.execute(query, (kodiid, mediatype))
# Add tags
self.logMsg("Adding Tags: %s" % tags, 2)
log("Adding Tags: %s" % tags, 2)
for tag in tags:
self.addTag(kodiid, tag, mediatype)
@ -750,7 +748,7 @@ class Kodidb_Functions():
except TypeError:
# Create the tag, because it does not exist
tag_id = self.createTag(tag)
self.logMsg("Adding tag: %s" % tag, 2)
log("Adding tag: %s" % tag, 2)
finally:
# Assign tag to item
@ -779,7 +777,7 @@ class Kodidb_Functions():
except TypeError:
# Create the tag
tag_id = self.createTag(tag)
self.logMsg("Adding tag: %s" % tag, 2)
log("Adding tag: %s" % tag, 2)
finally:
# Assign tag to item
@ -815,7 +813,7 @@ class Kodidb_Functions():
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
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:
# Kodi Helix
query = ' '.join((
@ -835,13 +833,13 @@ class Kodidb_Functions():
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
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
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):
# Kodi Isengard, Jarvis, Krypton
@ -858,7 +856,7 @@ class Kodidb_Functions():
except Exception as e:
# The new tag we are going to apply already exists for this item
# delete current tag instead
self.logMsg("Exception: %s" % e, 1)
log("Exception: %s" % e, 1)
query = ' '.join((
"DELETE FROM tag_link",
@ -882,7 +880,7 @@ class Kodidb_Functions():
except Exception as e:
# The new tag we are going to apply already exists for this item
# delete current tag instead
self.logMsg("Exception: %s" % e, 1)
log("Exception: %s" % e, 1)
query = ' '.join((
"DELETE FROM taglinks",
@ -943,7 +941,7 @@ class Kodidb_Functions():
def createBoxset(self, boxsetname):
self.logMsg("Adding boxset: %s" % boxsetname, 2)
log("Adding boxset: %s" % boxsetname, 2)
query = ' '.join((
"SELECT idSet",

View file

@ -11,7 +11,7 @@ import clientinfo
import downloadutils
import embydb_functions as embydb
import playbackutils as pbutils
import utils
from utils import Logging, window, settings, kodiSQL
#################################################################################################
@ -21,27 +21,25 @@ class KodiMonitor(xbmc.Monitor):
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
self.doUtils = downloadutils.DownloadUtils()
self.logMsg("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)
log("Kodi monitor started.", 1)
def onScanStarted(self, library):
self.logMsg("Kodi library scan %s running." % library, 2)
log("Kodi library scan %s running." % library, 2)
if library == "video":
utils.window('emby_kodiScan', value="true")
window('emby_kodiScan', value="true")
def onScanFinished(self, library):
self.logMsg("Kodi library scan %s finished." % library, 2)
log("Kodi library scan %s finished." % library, 2)
if library == "video":
utils.window('emby_kodiScan', clear=True)
window('emby_kodiScan', clear=True)
def onSettingsChanged(self):
# Monitor emby settings
@ -50,7 +48,7 @@ class KodiMonitor(xbmc.Monitor):
'''currentPath = utils.settings('useDirectPaths')
if utils.window('emby_pluginpath') != currentPath:
# 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)
resp = xbmcgui.Dialog().yesno(
heading="Playback mode change detected",
@ -61,17 +59,17 @@ class KodiMonitor(xbmc.Monitor):
if resp:
utils.reset()'''
currentLog = utils.settings('logLevel')
if utils.window('emby_logLevel') != currentLog:
currentLog = settings('logLevel')
if window('emby_logLevel') != currentLog:
# The log level changed, set new prop
self.logMsg("New log level: %s" % currentLog, 1)
utils.window('emby_logLevel', value=currentLog)
log("New log level: %s" % currentLog, 1)
window('emby_logLevel', value=currentLog)
def onNotification(self, sender, method, data):
doUtils = self.doUtils
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:
data = json.loads(data,'utf-8')
@ -84,23 +82,23 @@ class KodiMonitor(xbmc.Monitor):
kodiid = item['id']
item_type = item['type']
except (KeyError, TypeError):
self.logMsg("Item is invalid for playstate update.", 1)
log("Item is invalid for playstate update.", 1)
else:
if ((utils.settings('useDirectPaths') == "1" and not item_type == "song") or
(item_type == "song" and utils.settings('enableMusic') == "true")):
if ((settings('useDirectPaths') == "1" and not item_type == "song") or
(item_type == "song" and settings('enableMusic') == "true")):
# Set up properties for player
embyconn = utils.kodiSQL('emby')
embyconn = kodiSQL('emby')
embycursor = embyconn.cursor()
emby_db = embydb.Embydb_Functions(embycursor)
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
try:
itemid = emby_dbitem[0]
except TypeError:
self.logMsg("No kodiid returned.", 1)
log("No kodiid returned.", 1)
else:
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
result = doUtils.downloadUrl(url)
self.logMsg("Item: %s" % result, 2)
log("Item: %s" % result, 2)
playurl = None
count = 0
@ -114,12 +112,10 @@ class KodiMonitor(xbmc.Monitor):
listItem = xbmcgui.ListItem()
playback = pbutils.PlaybackUtils(result)
if item_type == "song" and utils.settings('streamMusic') == "true":
utils.window('emby_%s.playmethod' % playurl,
value="DirectStream")
if item_type == "song" and settings('streamMusic') == "true":
window('emby_%s.playmethod' % playurl, value="DirectStream")
else:
utils.window('emby_%s.playmethod' % playurl,
value="DirectPlay")
window('emby_%s.playmethod' % playurl, value="DirectPlay")
# Set properties for player.py
playback.setProperties(playurl, listItem)
finally:
@ -134,31 +130,31 @@ class KodiMonitor(xbmc.Monitor):
kodiid = item['id']
item_type = item['type']
except (KeyError, TypeError):
self.logMsg("Item is invalid for playstate update.", 1)
log("Item is invalid for playstate update.", 1)
else:
# Send notification to the server.
embyconn = utils.kodiSQL('emby')
embyconn = kodiSQL('emby')
embycursor = embyconn.cursor()
emby_db = embydb.Embydb_Functions(embycursor)
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
try:
itemid = emby_dbitem[0]
except TypeError:
self.logMsg("Could not find itemid in emby database.", 1)
log("Could not find itemid in emby database.", 1)
else:
# 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
utils.window('emby_skipWatched%s' % itemid, clear=True)
window('emby_skipWatched%s' % itemid, clear=True)
else:
# notify the server
url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % itemid
if playcount != 0:
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:
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:
embycursor.close()
@ -172,7 +168,7 @@ class KodiMonitor(xbmc.Monitor):
kodiid = data['id']
type = data['type']
except (KeyError, TypeError):
self.logMsg("Item is invalid for emby deletion.", 1)
log("Item is invalid for emby deletion.", 1)
else:
# Send the delete action to the server.
embyconn = utils.kodiSQL('emby')
@ -182,19 +178,19 @@ class KodiMonitor(xbmc.Monitor):
try:
itemid = emby_dbitem[0]
except TypeError:
self.logMsg("Could not find itemid in emby database.", 1)
log("Could not find itemid in emby database.", 1)
else:
if utils.settings('skipContextMenu') != "true":
resp = xbmcgui.Dialog().yesno(
heading="Confirm delete",
line1="Delete file on Emby Server?")
if not resp:
self.logMsg("User skipped deletion.", 1)
log("User skipped deletion.", 1)
embycursor.close()
return
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")
finally:
embycursor.close()'''
@ -203,13 +199,13 @@ class KodiMonitor(xbmc.Monitor):
elif method == "System.OnWake":
# Allow network to wake up
xbmc.sleep(10000)
utils.window('emby_onWake', value="true")
window('emby_onWake', value="true")
elif method == "GUI.OnScreensaverDeactivated":
if utils.settings('dbSyncScreensaver') == "true":
if settings('dbSyncScreensaver') == "true":
xbmc.sleep(5000);
utils.window('emby_onWake', value="true")
window('emby_onWake', value="true")
elif method == "Playlist.OnClear":

View file

@ -20,6 +20,7 @@ import kodidb_functions as kodidb
import read_embyserver as embyserver
import userclient
import videonodes
from utils import Logging, window, settings, language as lang
##################################################################################################
@ -42,6 +43,9 @@ class LibrarySync(threading.Thread):
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
self.__dict__ = self._shared_state
self.monitor = xbmc.Monitor()
@ -54,26 +58,20 @@ class LibrarySync(threading.Thread):
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):
dialog = None
if utils.settings('dbSyncIndicator') == "true" or forced:
if settings('dbSyncIndicator') == "true" or forced:
dialog = xbmcgui.DialogProgressBG()
dialog.create("Emby for Kodi", title)
self.logMsg("Show progress dialog: %s" % title, 2)
log("Show progress dialog: %s" % title, 2)
return dialog
def startSync(self):
settings = utils.settings
# Run at start up - optional to use the server plugin
if settings('SyncInstallRunDone') == "true":
@ -88,7 +86,7 @@ class LibrarySync(threading.Thread):
for plugin in result:
if plugin['Name'] == "Emby.Kodi Sync Queue":
self.logMsg("Found server plugin.", 2)
log("Found server plugin.", 2)
completed = self.fastSync()
break
@ -103,37 +101,31 @@ class LibrarySync(threading.Thread):
def fastSync(self):
lastSync = utils.settings('LastIncrementalSync')
lastSync = settings('LastIncrementalSync')
if not lastSync:
lastSync = "2010-01-01T00:00:00Z"
lastSyncTime = utils.convertdate(lastSync)
self.logMsg("Last sync run: %s" % lastSyncTime, 1)
lastSyncTime = utils.convertDate(lastSync)
log("Last sync run: %s" % lastSyncTime, 1)
# get server RetentionDateTime
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:
retention_time = result['RetentionDateTime']
except (TypeError, KeyError):
retention_time = "2010-01-01T00:00:00Z"
'''
retention_time = utils.convertdate(retention_time)
self.logMsg("RetentionDateTime: %s" % retention_time, 1)
retention_time = utils.convertDate(retention_time)
log("RetentionDateTime: %s" % retention_time, 1)
# if last sync before retention time do a full sync
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
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:
processlist = {
@ -145,11 +137,11 @@ class LibrarySync(threading.Thread):
}
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
else:
self.logMsg("Fast sync changes: %s" % result, 1)
log("Fast sync changes: %s" % result, 1)
for action in processlist:
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")
try: # datetime fails when used more than once, TypeError
server_time = result['ServerDateTime']
server_time = utils.convertdate(server_time)
server_time = utils.convertDate(server_time)
except Exception as e:
# 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)
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:
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:
utils.settings('LastIncrementalSync', value=lastSync)
settings('LastIncrementalSync', value=lastSync)
def shouldStop(self):
# Checkpoint during the syncing process
if self.monitor.abortRequested():
return True
elif utils.window('emby_shouldStop') == "true":
elif window('emby_shouldStop') == "true":
return True
else: # Keep going
return False
def dbCommit(self, connection):
window = utils.window
# Central commit, verifies if Kodi database update is running
kodidb_scan = window('emby_kodiScan') == "true"
while kodidb_scan:
self.logMsg("Kodi scan is running. Waiting...", 1)
log("Kodi scan is running. Waiting...", 1)
kodidb_scan = window('emby_kodiScan') == "true"
if self.shouldStop():
self.logMsg("Commit unsuccessful. Sync terminated.", 1)
log("Commit unsuccessful. Sync terminated.", 1)
break
if self.monitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
self.logMsg("Commit unsuccessful.", 1)
log("Commit unsuccessful.", 1)
break
else:
connection.commit()
self.logMsg("Commit successful.", 1)
log("Commit successful.", 1)
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.
music_enabled = utils.settings('enableMusic') == "true"
music_enabled = settings('enableMusic') == "true"
xbmc.executebuiltin('InhibitIdleShutdown(true)')
screensaver = utils.getScreensaver()
@ -284,7 +271,7 @@ class LibrarySync(threading.Thread):
self.dbCommit(kodiconn)
embyconn.commit()
elapsedTime = datetime.now() - startTime
self.logMsg("SyncDatabase (finished %s in: %s)"
log("SyncDatabase (finished %s in: %s)"
% (itemtype, str(elapsedTime).split('.')[0]), 1)
else:
# Close the Kodi cursor
@ -312,7 +299,7 @@ class LibrarySync(threading.Thread):
musicconn.commit()
embyconn.commit()
elapsedTime = datetime.now() - startTime
self.logMsg("SyncDatabase (finished music in: %s)"
log("SyncDatabase (finished music in: %s)"
% (str(elapsedTime).split('.')[0]), 1)
musiccursor.close()
@ -335,7 +322,7 @@ class LibrarySync(threading.Thread):
xbmcgui.Dialog().notification(
heading="Emby for Kodi",
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",
sound=False)
return True
@ -378,7 +365,7 @@ class LibrarySync(threading.Thread):
if view['type'] == "mixed":
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
self.vnodes.clearProperties()
@ -415,7 +402,8 @@ class LibrarySync(threading.Thread):
'Limit': 1,
'IncludeItemTypes': emby_mediatypes[mediatype]
} # 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:
verifyitem = result['Items'][0]['Id']
except (TypeError, IndexError):
@ -430,14 +418,14 @@ class LibrarySync(threading.Thread):
# Take the userview, and validate the item belong to the view
if self.emby.verifyView(grouped_view['Id'], verifyitem):
# 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)
foldername = grouped_view['Name']
break
else:
# Unable to find a match, add the name to our sorted_view list
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
try:
@ -453,7 +441,7 @@ class LibrarySync(threading.Thread):
current_tagid = view[2]
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)
# Create playlist for the video library
if (foldername not in playlists and
@ -472,7 +460,7 @@ class LibrarySync(threading.Thread):
emby_db.addView(folderid, foldername, viewtype, tagid)
else:
self.logMsg(' '.join((
log(' '.join((
"Found viewid: %s" % folderid,
"viewname: %s" % current_viewname,
@ -488,7 +476,7 @@ class LibrarySync(threading.Thread):
# View was modified, update with latest info
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)
# Update view with new info
@ -556,20 +544,19 @@ class LibrarySync(threading.Thread):
utils.window('Emby.nodes.total', str(totalnodes))
# 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:
emby_db.removeView(view)
def movies(self, embycursor, kodicursor, pdialog):
lang = utils.language
# Get movies from emby
emby_db = embydb.Embydb_Functions(embycursor)
movies = itemtypes.Movies(embycursor, kodicursor)
views = emby_db.getView_byType('movies')
views += emby_db.getView_byType('mixed')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
##### PROCESS MOVIES #####
for view in views:
@ -604,7 +591,7 @@ class LibrarySync(threading.Thread):
count += 1
movies.add_update(embymovie, view['name'], view['id'])
else:
self.logMsg("Movies finished.", 2)
log("Movies finished.", 2)
##### PROCESS BOXSETS #####
@ -631,7 +618,7 @@ class LibrarySync(threading.Thread):
count += 1
movies.add_updateBoxset(boxset)
else:
self.logMsg("Boxsets finished.", 2)
log("Boxsets finished.", 2)
return True
@ -642,7 +629,7 @@ class LibrarySync(threading.Thread):
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
views = emby_db.getView_byType('musicvideos')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
for view in views:
@ -656,7 +643,7 @@ class LibrarySync(threading.Thread):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="%s %s..." % (utils.language(33019), viewName))
message="%s %s..." % (lang(33019), viewName))
# Initial or repair sync
all_embymvideos = self.emby.getMusicVideos(viewId, dialog=pdialog)
@ -679,7 +666,7 @@ class LibrarySync(threading.Thread):
count += 1
mvideos.add_update(embymvideo, viewName, viewId)
else:
self.logMsg("MusicVideos finished.", 2)
log("MusicVideos finished.", 2)
return True
@ -691,7 +678,7 @@ class LibrarySync(threading.Thread):
views = emby_db.getView_byType('tvshows')
views += emby_db.getView_byType('mixed')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
for view in views:
@ -702,7 +689,7 @@ class LibrarySync(threading.Thread):
if pdialog:
pdialog.update(
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)
total = all_embytvshows['TotalRecordCount']
@ -737,7 +724,7 @@ class LibrarySync(threading.Thread):
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
tvshows.add_updateEpisode(episode)
else:
self.logMsg("TVShows finished.", 2)
log("TVShows finished.", 2)
return True
@ -757,7 +744,7 @@ class LibrarySync(threading.Thread):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="%s %s..." % (utils.language(33021), itemtype))
message="%s %s..." % (lang(33021), itemtype))
all_embyitems = process[itemtype][0](dialog=pdialog)
total = all_embyitems['TotalRecordCount']
@ -778,7 +765,7 @@ class LibrarySync(threading.Thread):
process[itemtype][1](embyitem)
else:
self.logMsg("%s finished." % itemtype, 2)
log("%s finished." % itemtype, 2)
return True
@ -799,7 +786,7 @@ class LibrarySync(threading.Thread):
itemids.append(item['ItemId'])
items = itemids
self.logMsg("Queue %s: %s" % (process, items), 1)
log("Queue %s: %s" % (process, items), 1)
processlist[process].extend(items)
def incrementalSync(self):
@ -833,7 +820,7 @@ class LibrarySync(threading.Thread):
}
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])
del process[process_type][:] # Reset class list
@ -871,7 +858,7 @@ class LibrarySync(threading.Thread):
if update_embydb:
update_embydb = False
self.logMsg("Updating emby database.", 1)
log("Updating emby database.", 1)
embyconn.commit()
self.saveLastSync()
@ -880,8 +867,8 @@ class LibrarySync(threading.Thread):
self.forceLibraryUpdate = False
self.dbCommit(kodiconn)
self.logMsg("Updating video library.", 1)
utils.window('emby_kodiScan', value="true")
log("Updating video library.", 1)
window('emby_kodiScan', value="true")
xbmc.executebuiltin('UpdateLibrary(video)')
if pDialog:
@ -893,7 +880,7 @@ class LibrarySync(threading.Thread):
def compareDBVersion(self, current, minimum):
# 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(".")
minMajor, minMinor, minPatch = minimum.split(".")
@ -911,7 +898,7 @@ class LibrarySync(threading.Thread):
try:
self.run_internal()
except Exception as e:
utils.window('emby_dbScan', clear=True)
window('emby_dbScan', clear=True)
xbmcgui.Dialog().ok(
heading="Emby for Kodi",
line1=(
@ -922,14 +909,11 @@ class LibrarySync(threading.Thread):
def run_internal(self):
lang = utils.language
window = utils.window
settings = utils.settings
dialog = xbmcgui.Dialog()
startupComplete = False
self.logMsg("---===### Starting LibrarySync ###===---", 0)
log("---===### Starting LibrarySync ###===---", 0)
while not self.monitor.abortRequested():
@ -947,12 +931,12 @@ class LibrarySync(threading.Thread):
uptoDate = self.compareDBVersion(currentVersion, minVersion)
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)
resp = dialog.yesno("Emby for Kodi", lang(33022))
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))
else:
utils.reset()
@ -967,7 +951,7 @@ class LibrarySync(threading.Thread):
videoDb = utils.getKodiVideoDBPath()
if not xbmcvfs.exists(videoDb):
# Database does not exists
self.logMsg(
log(
"The current Kodi version is incompatible "
"with the Emby for Kodi add-on. Please visit "
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
@ -979,12 +963,12 @@ class LibrarySync(threading.Thread):
break
# Run start up sync
self.logMsg("Database version: %s" % settings('dbCreatedWithVersion'), 0)
self.logMsg("SyncDatabase (started)", 1)
log("Database version: %s" % settings('dbCreatedWithVersion'), 0)
log("SyncDatabase (started)", 1)
startTime = datetime.now()
librarySync = self.startSync()
elapsedTime = datetime.now() - startTime
self.logMsg("SyncDatabase (finished in: %s) %s"
log("SyncDatabase (finished in: %s) %s"
% (str(elapsedTime).split('.')[0], librarySync), 1)
# Only try the initial sync once per kodi session regardless
# This will prevent an infinite loop in case something goes wrong.
@ -999,32 +983,32 @@ class LibrarySync(threading.Thread):
# Set in kodimonitor.py
window('emby_onWake', clear=True)
if window('emby_syncRunning') != "true":
self.logMsg("SyncDatabase onWake (started)", 0)
log("SyncDatabase onWake (started)", 0)
librarySync = self.startSync()
self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0)
log("SyncDatabase onWake (finished) %s" % librarySync, 0)
if self.stop_thread:
# Set in service.py
self.logMsg("Service terminated thread.", 2)
log("Service terminated thread.", 2)
break
if self.monitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
break
self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
log("###===--- LibrarySync Stopped ---===###", 0)
def stopThread(self):
self.stop_thread = True
self.logMsg("Ending thread...", 2)
log("Ending thread...", 2)
def suspendThread(self):
self.suspend_thread = True
self.logMsg("Pausing thread...", 0)
log("Pausing thread...", 0)
def resumeThread(self):
self.suspend_thread = False
self.logMsg("Resuming thread...", 0)
log("Resuming thread...", 0)
class ManualSync(LibrarySync):
@ -1041,14 +1025,13 @@ class ManualSync(LibrarySync):
def movies(self, embycursor, kodicursor, pdialog):
lang = utils.language
# Get movies from emby
emby_db = embydb.Embydb_Functions(embycursor)
movies = itemtypes.Movies(embycursor, kodicursor)
views = emby_db.getView_byType('movies')
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
try:
@ -1095,7 +1078,7 @@ class ManualSync(LibrarySync):
# Only update if movie is not in Kodi or checksum is different
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)
total = len(updatelist)
del updatelist[:]
@ -1137,7 +1120,7 @@ class ManualSync(LibrarySync):
updatelist.append(itemid)
embyboxsets.append(boxset)
self.logMsg("Boxsets to update: %s" % updatelist, 1)
log("Boxsets to update: %s" % updatelist, 1)
total = len(updatelist)
if pdialog:
@ -1161,13 +1144,13 @@ class ManualSync(LibrarySync):
if kodimovie not in all_embymoviesIds:
movies.remove(kodimovie)
else:
self.logMsg("Movies compare finished.", 1)
log("Movies compare finished.", 1)
for boxset in all_kodisets:
if boxset not in all_embyboxsetsIds:
movies.remove(boxset)
else:
self.logMsg("Boxsets compare finished.", 1)
log("Boxsets compare finished.", 1)
return True
@ -1178,7 +1161,7 @@ class ManualSync(LibrarySync):
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
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
try:
@ -1201,7 +1184,7 @@ class ManualSync(LibrarySync):
if pdialog:
pdialog.update(
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)
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
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)
total = len(updatelist)
del updatelist[:]
@ -1245,20 +1228,19 @@ class ManualSync(LibrarySync):
if kodimvideo not in all_embymvideosIds:
mvideos.remove(kodimvideo)
else:
self.logMsg("MusicVideos compare finished.", 1)
log("MusicVideos compare finished.", 1)
return True
def tvshows(self, embycursor, kodicursor, pdialog):
lang = utils.language
# Get shows from emby
emby_db = embydb.Embydb_Functions(embycursor)
tvshows = itemtypes.TVShows(embycursor, kodicursor)
views = emby_db.getView_byType('tvshows')
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
try:
@ -1305,7 +1287,7 @@ class ManualSync(LibrarySync):
# Only update if movie is not in Kodi or checksum is different
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)
total = len(updatelist)
del updatelist[:]
@ -1349,7 +1331,7 @@ class ManualSync(LibrarySync):
# Only update if movie is not in Kodi or checksum is different
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)
total = len(updatelist)
del updatelist[:]
@ -1374,13 +1356,13 @@ class ManualSync(LibrarySync):
if koditvshow not in all_embytvshowsIds:
tvshows.remove(koditvshow)
else:
self.logMsg("TVShows compare finished.", 1)
log("TVShows compare finished.", 1)
for kodiepisode in all_kodiepisodes:
if kodiepisode not in all_embyepisodesIds:
tvshows.remove(kodiepisode)
else:
self.logMsg("Episodes compare finished.", 1)
log("Episodes compare finished.", 1)
return True
@ -1421,7 +1403,7 @@ class ManualSync(LibrarySync):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="%s %s..." % (utils.language(33031), data_type))
message="%s %s..." % (lang(33031), data_type))
if data_type != "artists":
all_embyitems = process[data_type][0](basic=True, dialog=pdialog)
else:
@ -1446,7 +1428,7 @@ class ManualSync(LibrarySync):
if all_kodisongs.get(itemid) != API.getChecksum():
# Only update if songs is not in Kodi or checksum is different
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)
total = len(updatelist)
del updatelist[:]
@ -1467,15 +1449,15 @@ class ManualSync(LibrarySync):
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
music.remove(kodiartist)
else:
self.logMsg("Artist compare finished.", 1)
log("Artist compare finished.", 1)
for kodialbum in all_kodialbums:
if kodialbum not in all_embyalbumsIds:
music.remove(kodialbum)
else:
self.logMsg("Albums compare finished.", 1)
log("Albums compare finished.", 1)
for kodisong in all_kodisongs:
if kodisong not in all_embysongsIds:
music.remove(kodisong)
else:
self.logMsg("Songs compare finished.", 1)
return True
log("Songs compare finished.", 1)
return True

View file

@ -14,20 +14,18 @@ from mutagen import id3
import base64
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
def logMsg(msg, lvl=1):
utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl)
log = Logging('MusicTools').log
def getRealFileName(filename, isTemp=False):
#get the filename path accessible by python if possible...
if not xbmcvfs.exists(filename):
logMsg( "File does not exist! %s" %(filename), 0)
log("File does not exist! %s" % filename, 0)
return (False, "")
#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:
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
updateEmbyRating = False
@ -171,7 +169,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei
if updateEmbyRating and enableexportsongrating:
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
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)
return (rating, comment, hasEmbeddedCover)
@ -183,7 +181,7 @@ def getSongTags(file):
hasEmbeddedCover = False
isTemp,filename = getRealFileName(file)
logMsg( "getting song ID3 tags for " + filename)
log( "getting song ID3 tags for " + filename)
try:
###### FLAC FILES #############
@ -217,14 +215,14 @@ def getSongTags(file):
#POPM rating is 0-255 and needs to be converted to 0-5 range
if rating > 5: rating = (rating / 255) * 5
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
rating = int(round(rating,0))
except Exception as e:
#file in use ?
utils.logMsg("Exception in getSongTags", str(e),0)
log("Exception in getSongTags", str(e),0)
rating = None
#remove tempfile if needed....
@ -246,7 +244,7 @@ def updateRatingToFile(rating, file):
xbmcvfs.copy(file, tempfile)
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:
return
@ -263,7 +261,7 @@ def updateRatingToFile(rating, file):
audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
audio.save()
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
#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.copy(tempfile,file)
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
xbmcvfs.delete(tempfile)
except Exception as e:
#file in use ?
logMsg("Exception in updateRatingToFile %s" %e,0)
log("Exception in updateRatingToFile %s" %e,0)

View file

@ -7,11 +7,11 @@ import json
import xbmc
import xbmcgui
import utils
import clientinfo
import downloadutils
import kodidb_functions as kodidb
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):
global log
log = Logging(self.__class__.__name__).log
self.__dict__ = self._shared_state
self.clientInfo = clientinfo.ClientInfo()
@ -36,20 +39,13 @@ class Player(xbmc.Player):
self.ws = wsc.WebSocket_Client()
self.xbmcplayer = xbmc.Player()
self.logMsg("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)
log("Starting playback monitor.", 2)
def GetPlayStats(self):
return self.playStats
def onPlayBackStarted(self):
window = utils.window
# Will be called when xbmc starts playing a file
self.stopAll()
@ -67,7 +63,7 @@ class Player(xbmc.Player):
except: pass
if count == 5: # try 5 times
self.logMsg("Cancelling playback report...", 1)
log("Cancelling playback report...", 1)
break
else: count += 1
@ -84,12 +80,12 @@ class Player(xbmc.Player):
xbmc.sleep(200)
itemId = window("emby_%s.itemid" % currentFile)
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
else: tryCount += 1
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.
embyitem = "emby_%s" % currentFile
@ -102,7 +98,7 @@ class Player(xbmc.Player):
customseek = window('emby_customPlaylist.seektime')
if window('emby_customPlaylist') == "true" and customseek:
# 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)
window('emby_customPlaylist.seektime', clear=True)
@ -189,7 +185,7 @@ class Player(xbmc.Player):
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)
if externalIndex.get(str(indexSubs)):
@ -207,7 +203,7 @@ class Player(xbmc.Player):
# 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")
# Ensure we do have a runtime
@ -215,7 +211,7 @@ class Player(xbmc.Player):
runtime = int(runtime)
except ValueError:
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
data = {
@ -232,7 +228,7 @@ class Player(xbmc.Player):
}
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
'''if(itemType != None):
@ -251,7 +247,7 @@ class Player(xbmc.Player):
def reportPlayback(self):
self.logMsg("reportPlayback Called", 2)
log("reportPlayback Called", 2)
# Get current file
currentFile = self.currentFile
@ -345,11 +341,11 @@ class Player(xbmc.Player):
# Number of audiotracks to help get Emby Index
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
mapping = utils.window("emby_%s.indexMapping" % currentFile)
mapping = window("emby_%s.indexMapping" % currentFile)
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)
if externalIndex.get(str(indexSubs)):
@ -369,13 +365,13 @@ class Player(xbmc.Player):
# Report progress via websocketclient
postdata = json.dumps(postdata)
self.logMsg("Report: %s" % postdata, 2)
log("Report: %s" % postdata, 2)
self.ws.sendProgressUpdate(postdata)
def onPlayBackPaused(self):
currentFile = self.currentFile
self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
log("PLAYBACK_PAUSED: %s" % currentFile, 2)
if self.played_info.get(currentFile):
self.played_info[currentFile]['paused'] = True
@ -385,7 +381,7 @@ class Player(xbmc.Player):
def onPlayBackResumed(self):
currentFile = self.currentFile
self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
log("PLAYBACK_RESUMED: %s" % currentFile, 2)
if self.played_info.get(currentFile):
self.played_info[currentFile]['paused'] = False
@ -395,7 +391,7 @@ class Player(xbmc.Player):
def onPlayBackSeek(self, time, seekOffset):
# Make position when seeking a bit more accurate
currentFile = self.currentFile
self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
log("PLAYBACK_SEEK: %s" % currentFile, 2)
if self.played_info.get(currentFile):
position = self.xbmcplayer.getTime()
@ -404,39 +400,34 @@ class Player(xbmc.Player):
self.reportPlayback()
def onPlayBackStopped(self):
window = utils.window
# 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.seektime', clear=True)
window('emby_playbackProps', clear=True)
self.logMsg("Clear playlist properties.", 1)
log("Clear playlist properties.", 1)
self.stopAll()
def onPlayBackEnded(self):
# Will be called when xbmc stops playing a file
self.logMsg("ONPLAYBACK_ENDED", 2)
utils.window('emby_customPlaylist.seektime', clear=True)
log("ONPLAYBACK_ENDED", 2)
window('emby_customPlaylist.seektime', clear=True)
self.stopAll()
def stopAll(self):
lang = utils.language
settings = utils.settings
if not self.played_info:
return
self.logMsg("Played_information: %s" % self.played_info, 1)
log("Played_information: %s" % self.played_info, 1)
# Process each items
for item in self.played_info:
data = self.played_info.get(item)
if data:
self.logMsg("Item path: %s" % item, 2)
self.logMsg("Item data: %s" % data, 2)
log("Item path: %s" % item, 2)
log("Item data: %s" % data, 2)
runtime = data['runtime']
currentPosition = data['currentPosition']
@ -447,7 +438,7 @@ class Player(xbmc.Player):
playMethod = data['playmethod']
# 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:
try:
@ -457,7 +448,7 @@ class Player(xbmc.Player):
percentComplete = 0
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)
# Send the delete action to the server.
@ -475,18 +466,18 @@ class Player(xbmc.Player):
if percentComplete >= markPlayedAt and offerDelete:
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000)
if not resp:
self.logMsg("User skipped deletion.", 1)
log("User skipped deletion.", 1)
continue
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.stopPlayback(data)
# Stop transcoding
if playMethod == "Transcode":
self.logMsg("Transcoding for %s terminated." % itemid, 1)
log("Transcoding for %s terminated." % itemid, 1)
deviceId = self.clientInfo.getDeviceId()
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
self.doUtils(url, action_type="DELETE")
@ -495,7 +486,7 @@ class Player(xbmc.Player):
def stopPlayback(self, data):
self.logMsg("stopPlayback called", 2)
log("stopPlayback called", 2)
itemId = data['item_id']
currentPosition = data['currentPosition']

View file

@ -188,7 +188,7 @@ def setScreensaver(value):
result = xbmc.executeJSONRPC(json.dumps(query))
log("Toggling screensaver: %s %s" % (value, result), 1)
def convertdate(date):
def convertDate(date):
try:
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
except TypeError: