Centralized logging

This commit is contained in:
angelblue05 2016-06-16 00:43:36 -05:00
parent f5404fa1c1
commit 7a0f69e014
9 changed files with 542 additions and 516 deletions

View file

@ -5,7 +5,7 @@
##################################################################################################
import clientinfo
import utils
from utils import Logging, settings
##################################################################################################
@ -13,17 +13,16 @@ import utils
class API():
def __init__(self, item):
global log
log = Logging(self.__class__.__name__).log
# item is the api response
self.item = item
self.clientinfo = clientinfo.ClientInfo()
self.addonName = self.clientinfo.getAddonName()
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
def getUserData(self):
# Default
@ -223,7 +222,7 @@ class API():
resume = 0
if resume_seconds:
resume = round(float(resume_seconds), 6)
jumpback = int(utils.settings('resumeJumpBack'))
jumpback = int(settings('resumeJumpBack'))
if resume > jumpback:
# To avoid negative bookmark
resume = resume - jumpback

View file

@ -12,9 +12,9 @@ import xbmc
import xbmcgui
import xbmcvfs
import utils
import clientinfo
import image_cache_thread
from utils import Logging, window, settings, kodiSQL
#################################################################################################
@ -29,24 +29,25 @@ class Artwork():
imageCacheThreads = []
imageCacheLimitThreads = 0
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
self.clientinfo = clientinfo.ClientInfo()
self.addonName = self.clientinfo.getAddonName()
self.enableTextureCache = utils.settings('enableTextureCache') == "true"
self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit"))
self.enableTextureCache = settings('enableTextureCache') == "true"
self.imageCacheLimitThreads = int(settings('imageCacheLimit'))
self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5)
utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1)
log("Using Image Cache Thread Count: %s" % self.imageCacheLimitThreads, 1)
if not self.xbmc_port and self.enableTextureCache:
self.setKodiWebServerDetails()
self.userId = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userId)
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
self.userId = window('emby_currUser')
self.server = window('emby_server%s' % self.userId)
def double_urlencode(self, text):
@ -56,8 +57,8 @@ class Artwork():
return text
def single_urlencode(self, text):
text = urllib.urlencode({'blahblahblah':text.encode("utf-8")}) #urlencode needs a utf- string
# urlencode needs a utf- string
text = urllib.urlencode({'blahblahblah':text.encode("utf-8")})
text = text[13:]
return text.decode("utf-8") #return the result again as unicode
@ -167,102 +168,116 @@ class Artwork():
def FullTextureCacheSync(self):
# This method will sync all Kodi artwork to textures13.db
# and cache them locally. This takes diskspace!
dialog = xbmcgui.Dialog()
if not xbmcgui.Dialog().yesno("Image Texture Cache", "Running the image cache process can take some time.", "Are you sure you want continue?"):
if not dialog.yesno(
heading="Image Texture Cache",
line1=(
"Running the image cache process can take some time. "
"Are you sure you want continue?")):
return
self.logMsg("Doing Image Cache Sync", 1)
log("Doing Image Cache Sync", 1)
dialog = xbmcgui.DialogProgress()
dialog.create("Emby for Kodi", "Image Cache Sync")
pdialog = xbmcgui.DialogProgress()
pdialog.create("Emby for Kodi", "Image Cache Sync")
# ask to rest all existing or not
if xbmcgui.Dialog().yesno("Image Texture Cache", "Reset all existing cache data first?", ""):
self.logMsg("Resetting all cache data first", 1)
if dialog.yesno("Image Texture Cache", "Reset all existing cache data first?"):
log("Resetting all cache data first.", 1)
# Remove all existing textures first
path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
path = xbmc.translatePath('special://thumbnails/').decode('utf-8')
if xbmcvfs.exists(path):
allDirs, allFiles = xbmcvfs.listdir(path)
for dir in allDirs:
allDirs, allFiles = xbmcvfs.listdir(path+dir)
for file in allFiles:
if os.path.supports_unicode_filenames:
xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8')))
path = os.path.join(path+dir.decode('utf-8'),file.decode('utf-8'))
xbmcvfs.delete(path)
else:
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
# remove all existing data from texture DB
textureconnection = utils.kodiSQL('texture')
texturecursor = textureconnection.cursor()
texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = texturecursor.fetchall()
connection = kodiSQL('texture')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tableName = row[0]
if(tableName != "version"):
texturecursor.execute("DELETE FROM " + tableName)
textureconnection.commit()
texturecursor.close()
if tableName != "version":
cursor.execute("DELETE FROM " + tableName)
connection.commit()
cursor.close()
# Cache all entries in video DB
connection = utils.kodiSQL('video')
connection = kodiSQL('video')
cursor = connection.cursor()
cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
result = cursor.fetchall()
total = len(result)
count = 1
percentage = 0
self.logMsg("Image cache sync about to process " + str(total) + " images", 1)
for url in result:
if dialog.iscanceled():
break
percentage = int((float(count) / float(total))*100)
textMessage = str(count) + " of " + str(total) + " (" + str(len(self.imageCacheThreads)) + ")"
dialog.update(percentage, "Updating Image Cache: " + textMessage)
self.CacheTexture(url[0])
count += 1
log("Image cache sync about to process %s images" % total, 1)
cursor.close()
count = 0
for url in result:
if pdialog.iscanceled():
break
percentage = int((float(count) / float(total))*100)
message = "%s of %s (%s)" % (count, total, self.imageCacheThreads)
pdialog.update(percentage, "Updating Image Cache: %s" % message)
self.cacheTexture(url[0])
count += 1
# Cache all entries in music DB
connection = utils.kodiSQL('music')
connection = kodiSQL('music')
cursor = connection.cursor()
cursor.execute("SELECT url FROM art")
result = cursor.fetchall()
total = len(result)
count = 1
percentage = 0
self.logMsg("Image cache sync about to process " + str(total) + " images", 1)
for url in result:
if dialog.iscanceled():
break
percentage = int((float(count) / float(total))*100)
textMessage = str(count) + " of " + str(total)
dialog.update(percentage, "Updating Image Cache: " + textMessage)
self.CacheTexture(url[0])
count += 1
log("Image cache sync about to process %s images" % total, 1)
cursor.close()
dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
self.logMsg("Waiting for all threads to exit", 1)
while len(self.imageCacheThreads) > 0:
count = 0
for url in result:
if pdialog.iscanceled():
break
percentage = int((float(count) / float(total))*100)
message = "%s of %s" % (count, total)
pdialog.update(percentage, "Updating Image Cache: %s" % message)
self.cacheTexture(url[0])
count += 1
pdialog.update(100, "Waiting for all threads to exit: %s" % len(self.imageCacheThreads))
log("Waiting for all threads to exit", 1)
while len(self.imageCacheThreads):
for thread in self.imageCacheThreads:
if thread.isFinished:
self.imageCacheThreads.remove(thread)
dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
self.logMsg("Waiting for all threads to exit: " + str(len(self.imageCacheThreads)), 1)
pdialog.update(100, "Waiting for all threads to exit: %s" % len(self.imageCacheThreads))
log("Waiting for all threads to exit: %s" % len(self.imageCacheThreads), 1)
xbmc.sleep(500)
dialog.close()
pdialog.close()
def addWorkerImageCacheThread(self, urlToAdd):
def addWorkerImageCacheThread(self, url):
while(True):
while True:
# removed finished
for thread in self.imageCacheThreads:
if thread.isFinished:
self.imageCacheThreads.remove(thread)
# add a new thread or wait and retry if we hit our limit
if(len(self.imageCacheThreads) < self.imageCacheLimitThreads):
if len(self.imageCacheThreads) < self.imageCacheLimitThreads:
newThread = image_cache_thread.image_cache_thread()
newThread.setUrl(self.double_urlencode(urlToAdd))
newThread.setHost(self.xbmc_host, self.xbmc_port)
@ -271,23 +286,21 @@ class Artwork():
self.imageCacheThreads.append(newThread)
return
else:
self.logMsg("Waiting for empty queue spot: " + str(len(self.imageCacheThreads)), 2)
log("Waiting for empty queue spot: %s" % len(self.imageCacheThreads), 2)
xbmc.sleep(50)
def CacheTexture(self, url):
def cacheTexture(self, url):
# Cache a single image url to the texture cache
if url and self.enableTextureCache:
self.logMsg("Processing: %s" % url, 2)
log("Processing: %s" % url, 2)
if(self.imageCacheLimitThreads == 0 or self.imageCacheLimitThreads == None):
#Add image to texture cache by simply calling it at the http endpoint
if not self.imageCacheLimitThreads:
# Add image to texture cache by simply calling it at the http endpoint
url = self.double_urlencode(url)
try: # Extreme short timeouts so we will have a exception.
response = requests.head(
url=(
"http://%s:%s/image/image://%s"
url=("http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, url)),
auth=(self.xbmc_username, self.xbmc_password),
timeout=(0.01, 0.01))
@ -298,7 +311,7 @@ class Artwork():
self.addWorkerImageCacheThread(url)
def addArtwork(self, artwork, kodiId, mediaType, cursor):
def addArtwork(self, artwork, kodi_id, media_type, cursor):
# Kodi conversion table
kodiart = {
@ -329,7 +342,7 @@ class Artwork():
"AND media_type = ?",
"AND type LIKE ?"
))
cursor.execute(query, (kodiId, mediaType, "fanart%",))
cursor.execute(query, (kodi_id, media_type, "fanart%",))
rows = cursor.fetchall()
if len(rows) > backdropsNumber:
@ -341,16 +354,16 @@ class Artwork():
"AND media_type = ?",
"AND type LIKE ?"
))
cursor.execute(query, (kodiId, mediaType, "fanart_",))
cursor.execute(query, (kodi_id, media_type, "fanart_",))
# Process backdrops and extra fanart
index = ""
for backdrop in backdrops:
self.addOrUpdateArt(
imageUrl=backdrop,
kodiId=kodiId,
mediaType=mediaType,
imageType="%s%s" % ("fanart", index),
image_url=backdrop,
kodi_id=kodi_id,
media_type=media_type,
image_type="%s%s" % ("fanart", index),
cursor=cursor)
if backdropsNumber > 1:
@ -363,24 +376,24 @@ class Artwork():
# Primary art is processed as thumb and poster for Kodi.
for artType in kodiart[art]:
self.addOrUpdateArt(
imageUrl=artwork[art],
kodiId=kodiId,
mediaType=mediaType,
imageType=artType,
image_url=artwork[art],
kodi_id=kodi_id,
media_type=media_type,
image_type=artType,
cursor=cursor)
elif kodiart.get(art):
# Process the rest artwork type that Kodi can use
self.addOrUpdateArt(
imageUrl=artwork[art],
kodiId=kodiId,
mediaType=mediaType,
imageType=kodiart[art],
image_url=artwork[art],
kodi_id=kodi_id,
media_type=media_type,
image_type=kodiart[art],
cursor=cursor)
def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor):
def addOrUpdateArt(self, image_url, kodi_id, media_type, image_type, cursor):
# Possible that the imageurl is an empty string
if imageUrl:
if image_url:
cacheimage = False
query = ' '.join((
@ -391,13 +404,13 @@ class Artwork():
"AND media_type = ?",
"AND type = ?"
))
cursor.execute(query, (kodiId, mediaType, imageType,))
cursor.execute(query, (kodi_id, media_type, image_type,))
try: # Update the artwork
url = cursor.fetchone()[0]
except TypeError: # Add the artwork
cacheimage = True
self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 2)
log("Adding Art Link for kodiId: %s (%s)" % (kodi_id, image_url), 2)
query = (
'''
@ -406,21 +419,20 @@ class Artwork():
VALUES (?, ?, ?, ?)
'''
)
cursor.execute(query, (kodiId, mediaType, imageType, imageUrl))
cursor.execute(query, (kodi_id, media_type, image_type, image_url))
else: # Only cache artwork if it changed
if url != imageUrl:
if url != image_url:
cacheimage = True
# Only for the main backdrop, poster
if (utils.window('emby_initialScan') != "true" and
if (window('emby_initialScan') != "true" and
imageType in ("fanart", "poster")):
# Delete current entry before updating with the new one
self.deleteCachedArtwork(url)
self.logMsg(
"Updating Art url for %s kodiId: %s (%s) -> (%s)"
% (imageType, kodiId, url, imageUrl), 1)
log("Updating Art url for %s kodiId: %s (%s) -> (%s)"
% (image_type, kodi_id, url, image_url), 1)
query = ' '.join((
@ -430,13 +442,13 @@ class Artwork():
"AND media_type = ?",
"AND type = ?"
))
cursor.execute(query, (imageUrl, kodiId, mediaType, imageType))
cursor.execute(query, (image_url, kodi_id, media_type, image_type))
# Cache fanart and poster in Kodi texture cache
if cacheimage and imageType in ("fanart", "poster"):
self.CacheTexture(imageUrl)
if cacheimage and image_type in ("fanart", "poster"):
self.cacheTexture(image_url)
def deleteArtwork(self, kodiid, mediatype, cursor):
def deleteArtwork(self, kodi_id, media_type, cursor):
query = ' '.join((
@ -445,7 +457,7 @@ class Artwork():
"WHERE media_id = ?",
"AND media_type = ?"
))
cursor.execute(query, (kodiid, mediatype,))
cursor.execute(query, (kodi_id, media_type,))
rows = cursor.fetchall()
for row in rows:
@ -456,7 +468,7 @@ class Artwork():
def deleteCachedArtwork(self, url):
# Only necessary to remove and apply a new backdrop or poster
connection = utils.kodiSQL('texture')
connection = kodiSQL('texture')
cursor = connection.cursor()
try:
@ -464,21 +476,21 @@ class Artwork():
cachedurl = cursor.fetchone()[0]
except TypeError:
self.logMsg("Could not find cached url.", 1)
log("Could not find cached url.", 1)
except OperationalError:
self.logMsg("Database is locked. Skip deletion process.", 1)
log("Database is locked. Skip deletion process.", 1)
else: # Delete thumbnail as well as the entry
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cachedurl).decode('utf-8')
self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1)
log("Deleting cached thumbnail: %s" % thumbnails, 1)
xbmcvfs.delete(thumbnails)
try:
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
connection.commit()
except OperationalError:
self.logMsg("Issue deleting url from cache. Skipping.", 2)
log("Issue deleting url from cache. Skipping.", 2)
finally:
cursor.close()
@ -501,26 +513,26 @@ class Artwork():
return people
def getUserArtwork(self, itemid, itemtype):
def getUserArtwork(self, item_id, item_type):
# Load user information set by UserClient
image = ("%s/emby/Users/%s/Images/%s?Format=original"
% (self.server, itemid, itemtype))
% (self.server, item_id, item_type))
return image
def getAllArtwork(self, item, parentInfo=False):
def getAllArtwork(self, item, parent_artwork=False):
itemid = item['Id']
artworks = item['ImageTags']
backdrops = item.get('BackdropImageTags',[])
backdrops = item.get('BackdropImageTags', [])
maxHeight = 10000
maxWidth = 10000
customquery = ""
if utils.settings('compressArt') == "true":
if settings('compressArt') == "true":
customquery = "&Quality=90"
if utils.settings('enableCoverArt') == "false":
if settings('enableCoverArt') == "false":
customquery += "&EnableImageEnhancers=false"
allartworks = {
@ -554,7 +566,7 @@ class Artwork():
allartworks[art] = artwork
# Process parent items if the main item is missing artwork
if parentInfo:
if parent_artwork:
# Process parent backdrops
if not allartworks['Backdrop']:
@ -601,4 +613,4 @@ class Artwork():
% (self.server, parentId, maxWidth, maxHeight, parentTag, customquery))
allartworks['Primary'] = artwork
return allartworks
return allartworks

View file

@ -16,7 +16,7 @@ import downloadutils
import playutils as putils
import playlist
import read_embyserver as embyserver
import utils
from utils import Logging, window, settings, language as lang
#################################################################################################
@ -26,6 +26,9 @@ class PlaybackUtils():
def __init__(self, item):
global log
log = Logging(self.__class__.__name__).log
self.item = item
self.API = api.API(self.item)
@ -33,28 +36,20 @@ class PlaybackUtils():
self.addonName = self.clientInfo.getAddonName()
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.userid = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userid)
self.userid = window('emby_currUser')
self.server = window('emby_server%s' % self.userid)
self.artwork = artwork.Artwork()
self.emby = embyserver.Read_EmbyServer()
self.pl = playlist.Playlist()
def logMsg(self, msg, lvl=1):
self.className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
def play(self, itemid, dbid=None):
window = utils.window
settings = utils.settings
listitem = xbmcgui.ListItem()
playutils = putils.PlayUtils(self.item)
self.logMsg("Play called.", 1)
log("Play called.", 1)
playurl = playutils.getPlayUrl()
if not playurl:
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
@ -77,9 +72,9 @@ class PlaybackUtils():
introsPlaylist = False
dummyPlaylist = False
self.logMsg("Playlist start position: %s" % startPos, 2)
self.logMsg("Playlist plugin position: %s" % currentPosition, 2)
self.logMsg("Playlist size: %s" % sizePlaylist, 2)
log("Playlist start position: %s" % startPos, 2)
log("Playlist plugin position: %s" % currentPosition, 2)
log("Playlist size: %s" % sizePlaylist, 2)
############### RESUME POINT ################
@ -91,12 +86,11 @@ class PlaybackUtils():
if not propertiesPlayback:
window('emby_playbackProps', value="true")
self.logMsg("Setting up properties in playlist.", 1)
log("Setting up properties in playlist.", 1)
if (not homeScreen and not seektime and
window('emby_customPlaylist') != "true"):
if not homeScreen and not seektime and window('emby_customPlaylist') != "true":
self.logMsg("Adding dummy file to playlist.", 2)
log("Adding dummy file to playlist.", 2)
dummyPlaylist = True
playlist.add(playurl, listitem, index=startPos)
# Remove the original item from playlist
@ -116,18 +110,18 @@ class PlaybackUtils():
getTrailers = True
if settings('askCinema') == "true":
resp = xbmcgui.Dialog().yesno("Emby for Kodi", utils.language(33016))
resp = xbmcgui.Dialog().yesno("Emby for Kodi", lang(33016))
if not resp:
# User selected to not play trailers
getTrailers = False
self.logMsg("Skip trailers.", 1)
log("Skip trailers.", 1)
if getTrailers:
for intro in intros['Items']:
# The server randomly returns intros, process them.
introListItem = xbmcgui.ListItem()
introPlayurl = putils.PlayUtils(intro).getPlayUrl()
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
log("Adding Intro: %s" % introPlayurl, 1)
# Set listitem and properties for intros
pbutils = PlaybackUtils(intro)
@ -143,7 +137,7 @@ class PlaybackUtils():
if homeScreen and not seektime and not sizePlaylist:
# Extend our current playlist with the actual item to play
# only if there's no playlist first
self.logMsg("Adding main item to playlist.", 1)
log("Adding main item to playlist.", 1)
self.pl.addtoPlaylist(dbid, self.item['Type'].lower())
# Ensure that additional parts are played after the main item
@ -160,7 +154,7 @@ class PlaybackUtils():
additionalListItem = xbmcgui.ListItem()
additionalPlayurl = putils.PlayUtils(part).getPlayUrl()
self.logMsg("Adding additional part: %s" % partcount, 1)
log("Adding additional part: %s" % partcount, 1)
# Set listitem and properties for each additional parts
pbutils = PlaybackUtils(part)
@ -174,13 +168,13 @@ class PlaybackUtils():
if dummyPlaylist:
# Added a dummy file to the playlist,
# because the first item is going to fail automatically.
self.logMsg("Processed as a playlist. First item is skipped.", 1)
log("Processed as a playlist. First item is skipped.", 1)
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
# We just skipped adding properties. Reset flag for next time.
elif propertiesPlayback:
self.logMsg("Resetting properties playback flag.", 2)
log("Resetting properties playback flag.", 2)
window('emby_playbackProps', clear=True)
#self.pl.verifyPlaylist()
@ -190,7 +184,7 @@ class PlaybackUtils():
if window('emby_%s.playmethod' % playurl) == "Transcode":
# Filter ISO since Emby does not probe anymore
if self.item.get('VideoType') == "Iso":
self.logMsg("Skipping audio/subs prompt, ISO detected.", 1)
log("Skipping audio/subs prompt, ISO detected.", 1)
else:
playurl = playutils.audioSubsPref(playurl, listitem)
window('emby_%s.playmethod' % playurl, value="Transcode")
@ -201,23 +195,22 @@ class PlaybackUtils():
############### PLAYBACK ################
if homeScreen and seektime and window('emby_customPlaylist') != "true":
self.logMsg("Play as a widget item.", 1)
log("Play as a widget item.", 1)
self.setListItem(listitem)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
elif ((introsPlaylist and window('emby_customPlaylist') == "true") or
(homeScreen and not sizePlaylist)):
# Playlist was created just now, play it.
self.logMsg("Play playlist.", 1)
log("Play playlist.", 1)
xbmc.Player().play(playlist, startpos=startPos)
else:
self.logMsg("Play as a regular item.", 1)
log("Play as a regular item.", 1)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
def setProperties(self, playurl, listitem):
window = utils.window
# Set all properties necessary for plugin path playback
itemid = self.item['Id']
itemtype = self.item['Type']
@ -233,7 +226,7 @@ class PlaybackUtils():
window('%s.refreshid' % embyitem, value=itemid)
# Append external subtitles to stream
playmethod = utils.window('%s.playmethod' % embyitem)
playmethod = window('%s.playmethod' % embyitem)
# Only for direct stream
if playmethod in ("DirectStream"):
# Direct play automatically appends external
@ -272,13 +265,13 @@ class PlaybackUtils():
kodiindex += 1
mapping = json.dumps(mapping)
utils.window('emby_%s.indexMapping' % playurl, value=mapping)
window('emby_%s.indexMapping' % playurl, value=mapping)
return externalsubs
def setArtwork(self, listItem):
# Set up item and item info
allartwork = self.artwork.getAllArtwork(self.item, parentInfo=True)
allartwork = self.artwork.getAllArtwork(self.item, parent_artwork=True)
# Set artwork for listitem
arttypes = {

View file

@ -4,21 +4,22 @@
import xbmc
import utils
import clientinfo
import downloadutils
from utils import Logging, window, settings, kodiSQL
#################################################################################################
class Read_EmbyServer():
limitIndex = int(utils.settings('limitindex'))
limitIndex = int(settings('limitindex'))
def __init__(self):
window = utils.window
global log
log = Logging(self.__class__.__name__).log
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
@ -27,17 +28,11 @@ class Read_EmbyServer():
self.userId = window('emby_currUser')
self.server = window('emby_server%s' % self.userId)
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
def split_list(self, itemlist, size):
# Split up list in pieces of size. Will generate a list of lists
return [itemlist[i:i+size] for i in range(0, len(itemlist), size)]
def getItem(self, itemid):
# This will return the full item
item = {}
@ -60,7 +55,8 @@ class Read_EmbyServer():
'Ids': ",".join(itemlist),
'Fields': "Etag"
}
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)
if result:
items.extend(result['Items'])
@ -86,7 +82,8 @@ class Read_EmbyServer():
"MediaSources,VoteCount"
)
}
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)
if result:
items.extend(result['Items'])
@ -96,14 +93,15 @@ class Read_EmbyServer():
# Returns ancestors using embyId
viewId = None
for view in self.doUtils("{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid):
url = "{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid
for view in self.doUtils(url):
if view['Type'] == "CollectionFolder":
# Found view
viewId = view['Id']
# Compare to view table in emby database
emby = utils.kodiSQL('emby')
emby = kodiSQL('emby')
cursor_emby = emby.cursor()
query = ' '.join((
@ -124,7 +122,8 @@ class Read_EmbyServer():
return [viewName, viewId, mediatype]
def getFilteredSection(self, parentid, itemtype=None, sortby="SortName", recursive=True, limit=None, sortorder="Ascending", filter=""):
def getFilteredSection(self, parentid, itemtype=None, sortby="SortName", recursive=True,
limit=None, sortorder="Ascending", filter=""):
params = {
'ParentId': parentid,
@ -137,39 +136,54 @@ class Read_EmbyServer():
'SortBy': sortby,
'SortOrder': sortorder,
'Filters': filter,
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
'Fields': (
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
)
}
return self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
def getTvChannels(self):
params = {
'EnableImages': True,
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
'Fields': (
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
)
}
return self.doUtils("{server}/emby/LiveTv/Channels/?userid={UserId}&format=json", parameters=params)
url = "{server}/emby/LiveTv/Channels/?userid={UserId}&format=json"
return self.doUtils(url, parameters=params)
def getTvRecordings(self, groupid):
if groupid == "root": groupid = ""
if groupid == "root":
groupid = ""
params = {
'GroupId': groupid,
'EnableImages': True,
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
'Fields': (
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
)
}
return self.doUtils("{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json", parameters=params)
url = "{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json"
return self.doUtils(url, parameters=params)
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
@ -197,7 +211,7 @@ class Read_EmbyServer():
items['TotalRecordCount'] = total
except TypeError: # Failed to retrieve
self.logMsg("%s:%s Failed to retrieve the server response." % (url, params), 2)
log("%s:%s Failed to retrieve the server response." % (url, params), 2)
else:
index = 0
@ -239,27 +253,27 @@ class Read_EmbyServer():
# Something happened to the connection
if not throttled:
throttled = True
self.logMsg("Throttle activated.", 1)
log("Throttle activated.", 1)
if jump == highestjump:
# We already tried with the highestjump, but it failed. Reset value.
self.logMsg("Reset highest value.", 1)
log("Reset highest value.", 1)
highestjump = 0
# Lower the number by half
if highestjump:
throttled = False
jump = highestjump
self.logMsg("Throttle deactivated.", 1)
log("Throttle deactivated.", 1)
else:
jump = int(jump/4)
self.logMsg("Set jump limit to recover: %s" % jump, 2)
log("Set jump limit to recover: %s" % jump, 2)
retry = 0
while utils.window('emby_online') != "true":
while window('emby_online') != "true":
# Wait server to come back online
if retry == 5:
self.logMsg("Unable to reconnect to server. Abort process.", 1)
log("Unable to reconnect to server. Abort process.", 1)
return items
retry += 1
@ -287,7 +301,7 @@ class Read_EmbyServer():
increment = 10
jump += increment
self.logMsg("Increase jump limit to: %s" % jump, 1)
log("Increase jump limit to: %s" % jump, 1)
return items
def getViews(self, mediatype="", root=False, sortedlist=False):
@ -304,7 +318,7 @@ class Read_EmbyServer():
try:
items = result['Items']
except TypeError:
self.logMsg("Error retrieving views for type: %s" % mediatype, 2)
log("Error retrieving views for type: %s" % mediatype, 2)
else:
for item in items:
@ -373,15 +387,18 @@ class Read_EmbyServer():
return belongs
def getMovies(self, parentId, basic=False, dialog=None):
return self.getSection(parentId, "Movie", basic=basic, dialog=dialog)
def getBoxset(self, dialog=None):
return self.getSection(None, "BoxSet", dialog=dialog)
def getMovies_byBoxset(self, boxsetid):
return self.getSection(boxsetid, "Movie")
def getMusicVideos(self, parentId, basic=False, dialog=None):
return self.getSection(parentId, "MusicVideo", basic=basic, dialog=dialog)
def getHomeVideos(self, parentId):
@ -389,6 +406,7 @@ class Read_EmbyServer():
return self.getSection(parentId, "Video")
def getShows(self, parentId, basic=False, dialog=None):
return self.getSection(parentId, "Series", basic=basic, dialog=dialog)
def getSeasons(self, showId):
@ -404,7 +422,8 @@ class Read_EmbyServer():
'IsVirtualUnaired': False,
'Fields': "Etag"
}
result = self.doUtils("{server}/emby/Shows/%s/Seasons?UserId={UserId}&format=json" % showId, parameters=params)
url = "{server}/emby/Shows/%s/Seasons?UserId={UserId}&format=json" % showId
result = self.doUtils(url, parameters=params)
if result:
items = result
@ -422,7 +441,6 @@ class Read_EmbyServer():
return self.getSection(seasonId, "Episode")
def getArtists(self, dialog=None):
items = {
@ -444,7 +462,7 @@ class Read_EmbyServer():
items['TotalRecordCount'] = total
except TypeError: # Failed to retrieve
self.logMsg("%s:%s Failed to retrieve the server response." % (url, params), 2)
log("%s:%s Failed to retrieve the server response." % (url, params), 2)
else:
index = 1
@ -478,17 +496,20 @@ class Read_EmbyServer():
return items
def getAlbums(self, basic=False, dialog=None):
return self.getSection(None, "MusicAlbum", sortby="DateCreated", basic=basic, dialog=dialog)
def getAlbumsbyArtist(self, artistId):
return self.getSection(artistId, "MusicAlbum", sortby="DateCreated")
def getSongs(self, basic=False, dialog=None):
return self.getSection(None, "Audio", basic=basic, dialog=dialog)
def getSongsbyAlbum(self, albumId):
return self.getSection(albumId, "Audio")
return self.getSection(albumId, "Audio")
def getAdditionalParts(self, itemId):
@ -497,8 +518,8 @@ class Read_EmbyServer():
'Items': [],
'TotalRecordCount': 0
}
result = self.doUtils("{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId)
url = "{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId
result = self.doUtils(url)
if result:
items = result
@ -520,21 +541,27 @@ class Read_EmbyServer():
def updateUserRating(self, itemid, like=None, favourite=None, deletelike=False):
# Updates the user rating to Emby
doUtils = self.doUtils
if favourite:
self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="POST")
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
doUtils(url, action_type="POST")
elif favourite == False:
self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="DELETE")
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
doUtils(url, action_type="DELETE")
if not deletelike and like:
self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid, action_type="POST")
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid
doUtils(url, action_type="POST")
elif not deletelike and like is False:
self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid, action_type="POST")
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid
doUtils(url, action_type="POST")
elif deletelike:
self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid, action_type="DELETE")
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid
doUtils(url, action_type="DELETE")
else:
self.logMsg("Error processing user rating.", 1)
log("Error processing user rating.", 1)
self.logMsg("Update user rating to emby for itemid: %s "
"| like: %s | favourite: %s | deletelike: %s"
% (itemid, like, favourite, deletelike), 1)
log("Update user rating to emby for itemid: %s "
"| like: %s | favourite: %s | deletelike: %s"
% (itemid, like, favourite, deletelike), 1)

View file

@ -11,9 +11,9 @@ import xbmcaddon
import xbmcvfs
import artwork
import utils
import clientinfo
import downloadutils
from utils import Logging, window, settings, language as lang
##################################################################################################
@ -39,6 +39,9 @@ class UserClient(threading.Thread):
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
self.__dict__ = self._shared_state
self.addon = xbmcaddon.Addon()
@ -47,25 +50,20 @@ class UserClient(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 getAdditionalUsers(self):
additionalUsers = utils.settings('additionalUsers')
additionalUsers = settings('additionalUsers')
if additionalUsers:
self.AdditionalUser = additionalUsers.split(',')
def getUsername(self):
username = utils.settings('username')
username = settings('username')
if not username:
self.logMsg("No username saved.", 2)
log("No username saved.", 2)
return ""
return username
@ -73,7 +71,7 @@ class UserClient(threading.Thread):
def getLogLevel(self):
try:
logLevel = int(utils.settings('logLevel'))
logLevel = int(settings('logLevel'))
except ValueError:
logLevel = 0
@ -81,9 +79,6 @@ class UserClient(threading.Thread):
def getUserId(self):
window = utils.window
settings = utils.settings
username = self.getUsername()
w_userId = window('emby_currUser')
s_userId = settings('userId%s' % username)
@ -93,22 +88,20 @@ class UserClient(threading.Thread):
if not s_userId:
# Save access token if it's missing from settings
settings('userId%s' % username, value=w_userId)
self.logMsg("Returning userId from WINDOW for username: %s UserId: %s"
log("Returning userId from WINDOW for username: %s UserId: %s"
% (username, w_userId), 2)
return w_userId
# Verify the settings
elif s_userId:
self.logMsg("Returning userId from SETTINGS for username: %s userId: %s"
log("Returning userId from SETTINGS for username: %s userId: %s"
% (username, s_userId), 2)
return s_userId
# No userId found
else:
self.logMsg("No userId saved for username: %s." % username, 1)
log("No userId saved for username: %s." % username, 1)
def getServer(self, prefix=True):
settings = utils.settings
alternate = settings('altip') == "true"
if alternate:
# Alternate host
@ -124,7 +117,7 @@ class UserClient(threading.Thread):
server = host + ":" + port
if not host:
self.logMsg("No server information saved.", 2)
log("No server information saved.", 2)
return False
# If https is true
@ -141,9 +134,6 @@ class UserClient(threading.Thread):
def getToken(self):
window = utils.window
settings = utils.settings
username = self.getUsername()
userId = self.getUserId()
w_token = window('emby_accessToken%s' % userId)
@ -154,23 +144,21 @@ class UserClient(threading.Thread):
if not s_token:
# Save access token if it's missing from settings
settings('accessToken', value=w_token)
self.logMsg("Returning accessToken from WINDOW for username: %s accessToken: %s"
log("Returning accessToken from WINDOW for username: %s accessToken: %s"
% (username, w_token), 2)
return w_token
# Verify the settings
elif s_token:
self.logMsg("Returning accessToken from SETTINGS for username: %s accessToken: %s"
log("Returning accessToken from SETTINGS for username: %s accessToken: %s"
% (username, s_token), 2)
window('emby_accessToken%s' % username, value=s_token)
return s_token
else:
self.logMsg("No token found.", 1)
log("No token found.", 1)
return ""
def getSSLverify(self):
# Verify host certificate
settings = utils.settings
s_sslverify = settings('sslverify')
if settings('altip') == "true":
s_sslverify = settings('secondsslverify')
@ -182,8 +170,6 @@ class UserClient(threading.Thread):
def getSSL(self):
# Client side certificate
settings = utils.settings
s_cert = settings('sslcert')
if settings('altip') == "true":
s_cert = settings('secondsslcert')
@ -201,16 +187,16 @@ class UserClient(threading.Thread):
self.userSettings = result
# Set user image for skin display
if result.get('PrimaryImageTag'):
utils.window('EmbyUserImage', value=artwork.Artwork().getUserArtwork(result['Id'], 'Primary'))
window('EmbyUserImage', value=artwork.Artwork().getUserArtwork(result['Id'], 'Primary'))
# Set resume point max
result = doUtils("{server}/emby/System/Configuration?format=json")
utils.settings('markPlayed', value=str(result['MaxResumePct']))
settings('markPlayed', value=str(result['MaxResumePct']))
def getPublicUsers(self):
# Get public Users
result = self.doUtils.downloadUrl("%s/emby/Users/Public?format=json" % self.getServer(), authenticate=False)
url = "%s/emby/Users/Public?format=json" % self.getServer()
result = self.doUtils.downloadUrl(url, authenticate=False)
if result != "":
return result
else:
@ -220,13 +206,11 @@ class UserClient(threading.Thread):
def hasAccess(self):
# hasAccess is verified in service.py
window = utils.window
result = self.doUtils.downloadUrl("{server}/emby/Users?format=json")
if result == False:
# Access is restricted, set in downloadutils.py via exception
self.logMsg("Access is restricted.", 1)
log("Access is restricted.", 1)
self.HasAccess = False
elif window('emby_online') != "true":
@ -234,15 +218,13 @@ class UserClient(threading.Thread):
pass
elif window('emby_serverStatus') == "restricted":
self.logMsg("Access is granted.", 1)
log("Access is granted.", 1)
self.HasAccess = True
window('emby_serverStatus', clear=True)
xbmcgui.Dialog().notification("Emby for Kodi", utils.language(33007))
xbmcgui.Dialog().notification("Emby for Kodi", lang(33007))
def loadCurrUser(self, authenticated=False):
window = utils.window
doUtils = self.doUtils
username = self.getUsername()
userId = self.getUserId()
@ -290,9 +272,6 @@ class UserClient(threading.Thread):
def authenticate(self):
lang = utils.language
window = utils.window
settings = utils.settings
dialog = xbmcgui.Dialog()
# Get /profile/addon_data
@ -304,12 +283,12 @@ class UserClient(threading.Thread):
# If there's no settings.xml
if not hasSettings:
self.logMsg("No settings.xml found.", 1)
log("No settings.xml found.", 1)
self.auth = False
return
# If no user information
elif not server or not username:
self.logMsg("Missing server information.", 1)
log("Missing server information.", 1)
self.auth = False
return
# If there's a token, load the user
@ -319,9 +298,9 @@ class UserClient(threading.Thread):
if result is False:
pass
else:
self.logMsg("Current user: %s" % self.currUser, 1)
self.logMsg("Current userId: %s" % self.currUserId, 1)
self.logMsg("Current accessToken: %s" % self.currToken, 2)
log("Current user: %s" % self.currUser, 1)
log("Current userId: %s" % self.currUserId, 1)
log("Current accessToken: %s" % self.currToken, 2)
return
##### AUTHENTICATE USER #####
@ -341,7 +320,7 @@ class UserClient(threading.Thread):
option=xbmcgui.ALPHANUM_HIDE_INPUT)
# If password dialog is cancelled
if not password:
self.logMsg("No password entered.", 0)
log("No password entered.", 0)
window('emby_serverStatus', value="Stop")
self.auth = False
return
@ -356,16 +335,17 @@ class UserClient(threading.Thread):
# Authenticate username and password
data = {'username': username, 'password': sha1}
self.logMsg(data, 2)
log(data, 2)
result = self.doUtils.downloadUrl("%s/emby/Users/AuthenticateByName?format=json" % server, postBody=data, action_type="POST", authenticate=False)
url = "%s/emby/Users/AuthenticateByName?format=json" % server
result = self.doUtils.downloadUrl(url, postBody=data, action_type="POST", authenticate=False)
try:
self.logMsg("Auth response: %s" % result, 1)
log("Auth response: %s" % result, 1)
accessToken = result['AccessToken']
except (KeyError, TypeError):
self.logMsg("Failed to retrieve the api key.", 1)
log("Failed to retrieve the api key.", 1)
accessToken = None
if accessToken is not None:
@ -374,19 +354,19 @@ class UserClient(threading.Thread):
"%s %s!" % (lang(33000), self.currUser.decode('utf-8')))
settings('accessToken', value=accessToken)
settings('userId%s' % username, value=result['User']['Id'])
self.logMsg("User Authenticated: %s" % accessToken, 1)
log("User Authenticated: %s" % accessToken, 1)
self.loadCurrUser(authenticated=True)
window('emby_serverStatus', clear=True)
self.retry = 0
else:
self.logMsg("User authentication failed.", 1)
log("User authentication failed.", 1)
settings('accessToken', value="")
settings('userId%s' % username, value="")
dialog.ok(lang(33001), lang(33009))
# Give two attempts at entering password
if self.retry == 2:
self.logMsg("Too many retries. "
log("Too many retries. "
"You can retry by resetting attempts in the addon settings.", 1)
window('emby_serverStatus', value="Stop")
dialog.ok(lang(33001), lang(33010))
@ -396,23 +376,21 @@ class UserClient(threading.Thread):
def resetClient(self):
self.logMsg("Reset UserClient authentication.", 1)
log("Reset UserClient authentication.", 1)
if self.currToken is not None:
# In case of 401, removed saved token
utils.settings('accessToken', value="")
utils.window('emby_accessToken%s' % self.getUserId(), clear=True)
settings('accessToken', value="")
window('emby_accessToken%s' % self.getUserId(), clear=True)
self.currToken = None
self.logMsg("User token has been removed.", 1)
log("User token has been removed.", 1)
self.auth = True
self.currUser = None
def run(self):
window = utils.window
monitor = xbmc.Monitor()
self.logMsg("----===## Starting UserClient ##===----", 0)
log("----===## Starting UserClient ##===----", 0)
while not monitor.abortRequested():
@ -447,8 +425,8 @@ class UserClient(threading.Thread):
# The status Stop is for when user cancelled password dialog.
if server and username and status != "Stop":
# Only if there's information found to login
self.logMsg("Server found: %s" % server, 2)
self.logMsg("Username found: %s" % username, 2)
log("Server found: %s" % server, 2)
log("Username found: %s" % username, 2)
self.auth = True
@ -461,7 +439,7 @@ class UserClient(threading.Thread):
break
self.doUtils.stopSession()
self.logMsg("##===---- UserClient Stopped ----===##", 0)
log("##===---- UserClient Stopped ----===##", 0)
def stopClient(self):
# When emby for kodi terminates

View file

@ -20,7 +20,7 @@ import xbmcgui
import xbmcvfs
#################################################################################################
# Main methods
def logMsg(title, msg, level=1):
@ -43,43 +43,80 @@ def logMsg(title, msg, level=1):
except UnicodeEncodeError:
xbmc.log("%s -> %s" % (title, msg.encode('utf-8')))
def window(property, value=None, clear=False, windowid=10000):
# Get or set window property
WINDOW = xbmcgui.Window(windowid)
class Logging():
#setproperty accepts both string and unicode but utf-8 strings are adviced by kodi devs because some unicode can give issues
'''if isinstance(property, unicode):
property = property.encode("utf-8")
if isinstance(value, unicode):
value = value.encode("utf-8")'''
LOGGINGCLASS = None
def __init__(self, classname=""):
self.LOGGINGCLASS = classname
def log(self, msg, level=1):
self.logMsg("EMBY %s" % self.LOGGINGCLASS, msg, level)
def logMsg(self, title, msg, level=1):
# Get the logLevel set in UserClient
try:
logLevel = int(window('emby_logLevel'))
except ValueError:
logLevel = 0
if logLevel >= level:
if logLevel == 2: # inspect.stack() is expensive
try:
xbmc.log("%s -> %s : %s" % (title, inspect.stack()[1][3], msg))
except UnicodeEncodeError:
xbmc.log("%s -> %s : %s" % (title, inspect.stack()[1][3], msg.encode('utf-8')))
else:
try:
xbmc.log("%s -> %s" % (title, msg))
except UnicodeEncodeError:
xbmc.log("%s -> %s" % (title, msg.encode('utf-8')))
# Initiate class for utils.py document logging
log = Logging('Utils').log
def window(property, value=None, clear=False, window_id=10000):
# Get or set window property
WINDOW = xbmcgui.Window(window_id)
if clear:
WINDOW.clearProperty(property)
elif value is not None:
WINDOW.setProperty(property, value)
else: #getproperty returns string so convert to unicode
return WINDOW.getProperty(property)#.decode("utf-8")
else:
return WINDOW.getProperty(property)
def settings(setting, value=None):
# Get or add addon setting
addon = xbmcaddon.Addon(id='plugin.video.emby')
if value is not None:
xbmcaddon.Addon(id='plugin.video.emby').setSetting(setting, value)
else:
return xbmcaddon.Addon(id='plugin.video.emby').getSetting(setting) #returns unicode object
addon.setSetting(setting, value)
else: # returns unicode object
return addon.getSetting(setting)
def language(stringid):
# Central string retrieval
string = xbmcaddon.Addon(id='plugin.video.emby').getLocalizedString(stringid) #returns unicode object
def language(string_id):
# Central string retrieval - unicode
string = xbmcaddon.Addon(id='plugin.video.emby').getLocalizedString(string_id)
return string
#################################################################################################
# Database related methods
def kodiSQL(media_type="video"):
if media_type == "emby":
dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8')
elif media_type == "music":
dbPath = getKodiMusicDBPath()
elif media_type == "texture":
dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
elif media_type == "music":
dbPath = getKodiMusicDBPath()
else:
dbPath = getKodiVideoDBPath()
@ -98,8 +135,8 @@ def getKodiVideoDBPath():
}
dbPath = xbmc.translatePath(
"special://database/MyVideos%s.db"
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
"special://database/MyVideos%s.db"
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
return dbPath
def getKodiMusicDBPath():
@ -114,10 +151,13 @@ def getKodiMusicDBPath():
}
dbPath = xbmc.translatePath(
"special://database/MyMusic%s.db"
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
"special://database/MyMusic%s.db"
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
return dbPath
#################################################################################################
# Utility methods
def getScreensaver():
# Get the current screensaver value
query = {
@ -145,139 +185,8 @@ def setScreensaver(value):
'value': value
}
}
logMsg("EMBY", "Toggling screensaver: %s %s" % (value, xbmc.executeJSONRPC(json.dumps(query))), 1)
def reset():
dialog = xbmcgui.Dialog()
if dialog.yesno("Warning", "Are you sure you want to reset your local Kodi database?") == 0:
return
# first stop any db sync
window('emby_shouldStop', value="true")
count = 10
while window('emby_dbScan') == "true":
logMsg("EMBY", "Sync is running, will retry: %s..." % count)
count -= 1
if count == 0:
dialog.ok("Warning", "Could not stop the database from running. Try again.")
return
xbmc.sleep(1000)
# Clean up the playlists
deletePlaylists()
# Clean up the video nodes
deleteNodes()
# Wipe the kodi databases
logMsg("EMBY", "Resetting the Kodi video database.", 0)
connection = kodiSQL('video')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tablename = row[0]
if tablename != "version":
cursor.execute("DELETE FROM " + tablename)
connection.commit()
cursor.close()
if settings('enableMusic') == "true":
logMsg("EMBY", "Resetting the Kodi music database.")
connection = kodiSQL('music')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tablename = row[0]
if tablename != "version":
cursor.execute("DELETE FROM " + tablename)
connection.commit()
cursor.close()
# Wipe the emby database
logMsg("EMBY", "Resetting the Emby database.", 0)
connection = kodiSQL('emby')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tablename = row[0]
if tablename != "version":
cursor.execute("DELETE FROM " + tablename)
cursor.execute('DROP table IF EXISTS emby')
cursor.execute('DROP table IF EXISTS view')
connection.commit()
cursor.close()
# Offer to wipe cached thumbnails
resp = dialog.yesno("Warning", "Remove all cached artwork?")
if resp:
logMsg("EMBY", "Resetting all cached artwork.", 0)
# Remove all existing textures first
path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
if xbmcvfs.exists(path):
allDirs, allFiles = xbmcvfs.listdir(path)
for dir in allDirs:
allDirs, allFiles = xbmcvfs.listdir(path+dir)
for file in allFiles:
if os.path.supports_unicode_filenames:
xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8')))
else:
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
# remove all existing data from texture DB
connection = kodiSQL('texture')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tableName = row[0]
if(tableName != "version"):
cursor.execute("DELETE FROM " + tableName)
connection.commit()
cursor.close()
# reset the install run flag
settings('SyncInstallRunDone', value="false")
# Remove emby info
resp = dialog.yesno("Warning", "Reset all Emby Addon settings?")
if resp:
# Delete the settings
addon = xbmcaddon.Addon()
addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
dataPath = "%ssettings.xml" % addondir
xbmcvfs.delete(dataPath)
logMsg("EMBY", "Deleting: settings.xml", 1)
dialog.ok(
heading="Emby for Kodi",
line1="Database reset has completed, Kodi will now restart to apply the changes.")
xbmc.executebuiltin('RestartApp')
def profiling(sortby="cumulative"):
# Will print results to Kodi log
def decorator(func):
def wrapper(*args, **kwargs):
pr = cProfile.Profile()
pr.enable()
result = func(*args, **kwargs)
pr.disable()
s = StringIO.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
logMsg("EMBY Profiling", s.getvalue(), 1)
return result
return wrapper
return decorator
result = xbmc.executeJSONRPC(json.dumps(query))
log("Toggling screensaver: %s %s" % (value, result), 1)
def convertdate(date):
try:
@ -344,6 +253,141 @@ def indent(elem, level=0):
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def profiling(sortby="cumulative"):
# Will print results to Kodi log
def decorator(func):
def wrapper(*args, **kwargs):
pr = cProfile.Profile()
pr.enable()
result = func(*args, **kwargs)
pr.disable()
s = StringIO.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
log(s.getvalue(), 1)
return result
return wrapper
return decorator
#################################################################################################
# Addon utilities
def reset():
dialog = xbmcgui.Dialog()
if not dialog.yesno("Warning", "Are you sure you want to reset your local Kodi database?"):
return
# first stop any db sync
window('emby_shouldStop', value="true")
count = 10
while window('emby_dbScan') == "true":
logMsg("EMBY", "Sync is running, will retry: %s..." % count)
count -= 1
if count == 0:
dialog.ok("Warning", "Could not stop the database from running. Try again.")
return
xbmc.sleep(1000)
# Clean up the playlists
deletePlaylists()
# Clean up the video nodes
deleteNodes()
# Wipe the kodi databases
log("Resetting the Kodi video database.", 0)
connection = kodiSQL('video')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tablename = row[0]
if tablename != "version":
cursor.execute("DELETE FROM " + tablename)
connection.commit()
cursor.close()
if settings('enableMusic') == "true":
log("Resetting the Kodi music database.", 0)
connection = kodiSQL('music')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tablename = row[0]
if tablename != "version":
cursor.execute("DELETE FROM " + tablename)
connection.commit()
cursor.close()
# Wipe the emby database
log("Resetting the Emby database.", 0)
connection = kodiSQL('emby')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tablename = row[0]
if tablename != "version":
cursor.execute("DELETE FROM " + tablename)
cursor.execute('DROP table IF EXISTS emby')
cursor.execute('DROP table IF EXISTS view')
connection.commit()
cursor.close()
# Offer to wipe cached thumbnails
resp = dialog.yesno("Warning", "Remove all cached artwork?")
if resp:
log("Resetting all cached artwork.", 0)
# Remove all existing textures first
path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
if xbmcvfs.exists(path):
allDirs, allFiles = xbmcvfs.listdir(path)
for dir in allDirs:
allDirs, allFiles = xbmcvfs.listdir(path+dir)
for file in allFiles:
if os.path.supports_unicode_filenames:
xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8')))
else:
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
# remove all existing data from texture DB
connection = kodiSQL('texture')
cursor = connection.cursor()
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
rows = cursor.fetchall()
for row in rows:
tableName = row[0]
if(tableName != "version"):
cursor.execute("DELETE FROM " + tableName)
connection.commit()
cursor.close()
# reset the install run flag
settings('SyncInstallRunDone', value="false")
# Remove emby info
resp = dialog.yesno("Warning", "Reset all Emby Addon settings?")
if resp:
# Delete the settings
addon = xbmcaddon.Addon()
addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
dataPath = "%ssettings.xml" % addondir
xbmcvfs.delete(dataPath)
log("Deleting: settings.xml", 1)
dialog.ok(
heading="Emby for Kodi",
line1="Database reset has completed, Kodi will now restart to apply the changes.")
xbmc.executebuiltin('RestartApp')
def sourcesXML():
# To make Master lock compatible
path = xbmc.translatePath("special://profile/").decode('utf-8')
@ -413,12 +457,11 @@ def passwordsXML():
for path in paths:
if path.find('.//from').text == "smb://%s/" % credentials:
paths.remove(path)
logMsg("EMBY", "Successfully removed credentials for: %s"
% credentials, 1)
log("Successfully removed credentials for: %s" % credentials, 1)
etree.ElementTree(root).write(xmlpath)
break
else:
logMsg("EMBY", "Failed to find saved server: %s in passwords.xml" % credentials, 1)
log("Failed to find saved server: %s in passwords.xml" % credentials, 1)
settings('networkCreds', value="")
xbmcgui.Dialog().notification(
@ -473,7 +516,7 @@ def passwordsXML():
# Add credentials
settings('networkCreds', value="%s" % server)
logMsg("EMBY", "Added server: %s to passwords.xml" % server, 1)
log("Added server: %s to passwords.xml" % server, 1)
# Prettify and write to file
try:
indent(root)
@ -501,7 +544,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
# Create the playlist directory
if not xbmcvfs.exists(path):
logMsg("EMBY", "Creating directory: %s" % path, 1)
log("Creating directory: %s" % path, 1)
xbmcvfs.mkdirs(path)
# Only add the playlist if it doesn't already exists
@ -509,7 +552,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
if delete:
xbmcvfs.delete(xsppath)
logMsg("EMBY", "Successfully removed playlist: %s." % tagname, 1)
log("Successfully removed playlist: %s." % tagname, 1)
return
@ -517,11 +560,11 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
itemtypes = {
'homevideos': "movies"
}
logMsg("EMBY", "Writing playlist file to: %s" % xsppath, 1)
log("Writing playlist file to: %s" % xsppath, 1)
try:
f = xbmcvfs.File(xsppath, 'w')
except:
logMsg("EMBY", "Failed to create playlist: %s" % xsppath, 1)
log("Failed to create playlist: %s" % xsppath, 1)
return
else:
f.write(
@ -535,7 +578,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
'</smartplaylist>'
% (itemtypes.get(mediatype, mediatype), plname, tagname))
f.close()
logMsg("EMBY", "Successfully added playlist: %s" % tagname)
log("Successfully added playlist: %s" % tagname, 1)
def deletePlaylists():
@ -557,10 +600,10 @@ def deleteNodes():
try:
shutil.rmtree("%s%s" % (path, dir.decode('utf-8')))
except:
logMsg("EMBY", "Failed to delete directory: %s" % dir.decode('utf-8'))
log("Failed to delete directory: %s" % dir.decode('utf-8'), 0)
for file in files:
if file.decode('utf-8').startswith('emby'):
try:
xbmcvfs.delete("%s%s" % (path, file.decode('utf-8')))
except:
logMsg("EMBY", "Failed to file: %s" % file.decode('utf-8'))
log("Failed to file: %s" % file.decode('utf-8'), 0)

View file

@ -11,6 +11,7 @@ import xbmcvfs
import clientinfo
import utils
from utils import Logging, window, language as lang
#################################################################################################
@ -20,16 +21,14 @@ class VideoNodes(object):
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
clientInfo = clientinfo.ClientInfo()
self.addonName = clientInfo.getAddonName()
self.kodiversion = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
def commonRoot(self, order, label, tagname, roottype=1):
@ -54,8 +53,6 @@ class VideoNodes(object):
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False):
window = utils.window
if viewtype == "mixed":
dirname = "%s - %s" % (viewid, mediatype)
else:
@ -82,7 +79,7 @@ class VideoNodes(object):
for file in files:
xbmcvfs.delete(nodepath + file)
self.logMsg("Sucessfully removed videonode: %s." % tagname, 1)
log("Sucessfully removed videonode: %s." % tagname, 1)
return
# Create index entry
@ -184,7 +181,7 @@ class VideoNodes(object):
# Get label
stringid = nodes[node]
if node != "1":
label = utils.language(stringid)
label = lang(stringid)
if not label:
label = xbmc.getLocalizedString(stringid)
else:
@ -319,8 +316,6 @@ class VideoNodes(object):
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
window = utils.window
tagname = tagname.encode('utf-8')
cleantagname = utils.normalize_nodes(tagname)
nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
@ -342,7 +337,7 @@ class VideoNodes(object):
'Favorite tvshows': 30181,
'channels': 30173
}
label = utils.language(labels[tagname])
label = lang(labels[tagname])
embynode = "Emby.nodes.%s" % indexnumber
window('%s.title' % embynode, value=label)
window('%s.path' % embynode, value=windowpath)
@ -369,9 +364,7 @@ class VideoNodes(object):
def clearProperties(self):
window = utils.window
self.logMsg("Clearing nodes properties.", 1)
log("Clearing nodes properties.", 1)
embyprops = window('Emby.nodes.total')
propnames = [

View file

@ -14,10 +14,7 @@ import downloadutils
import librarysync
import playlist
import userclient
import utils
import logging
logging.basicConfig()
from utils import Logging, window, settings, language as lang
#################################################################################################
@ -32,6 +29,9 @@ class WebSocket_Client(threading.Thread):
def __init__(self):
global log
log = Logging(self.__class__.__name__).log
self.__dict__ = self._shared_state
self.monitor = xbmc.Monitor()
@ -43,15 +43,10 @@ class WebSocket_Client(threading.Thread):
threading.Thread.__init__(self)
def logMsg(self, msg, lvl=1):
self.className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
def sendProgressUpdate(self, data):
self.logMsg("sendProgressUpdate", 2)
log("sendProgressUpdate", 2)
try:
messageData = {
@ -60,23 +55,21 @@ class WebSocket_Client(threading.Thread):
}
messageString = json.dumps(messageData)
self.client.send(messageString)
self.logMsg("Message data: %s" % messageString, 2)
log("Message data: %s" % messageString, 2)
except Exception as e:
self.logMsg("Exception: %s" % e, 1)
log("Exception: %s" % e, 1)
def on_message(self, ws, message):
window = utils.window
lang = utils.language
result = json.loads(message)
messageType = result['MessageType']
data = result['Data']
dialog = xbmcgui.Dialog()
if messageType not in ('SessionEnded'):
# Mute certain events
self.logMsg("Message: %s" % message, 1)
log("Message: %s" % message, 1)
if messageType == "Play":
# A remote control play command has been sent from the server.
@ -84,7 +77,6 @@ class WebSocket_Client(threading.Thread):
command = data['PlayCommand']
pl = playlist.Playlist()
dialog = xbmcgui.Dialog()
if command == "PlayNow":
dialog.notification(
@ -126,10 +118,10 @@ class WebSocket_Client(threading.Thread):
seekto = data['SeekPositionTicks']
seektime = seekto / 10000000.0
action(seektime)
self.logMsg("Seek to %s." % seektime, 1)
log("Seek to %s." % seektime, 1)
else:
action()
self.logMsg("Command: %s completed." % command, 1)
log("Command: %s completed." % command, 1)
window('emby_command', value="true")
@ -199,11 +191,11 @@ class WebSocket_Client(threading.Thread):
header = arguments['Header']
text = arguments['Text']
xbmcgui.Dialog().notification(
heading=header,
message=text,
icon="special://home/addons/plugin.video.emby/icon.png",
time=4000)
dialog.notification(
heading=header,
message=text,
icon="special://home/addons/plugin.video.emby/icon.png",
time=4000)
elif command == "SendString":
@ -250,11 +242,11 @@ class WebSocket_Client(threading.Thread):
xbmc.executebuiltin(action)
elif messageType == "ServerRestarting":
if utils.settings('supressRestartMsg') == "true":
xbmcgui.Dialog().notification(
heading="Emby for Kodi",
message=lang(33006),
icon="special://home/addons/plugin.video.emby/icon.png")
if settings('supressRestartMsg') == "true":
dialog.notification(
heading="Emby for Kodi",
message=lang(33006),
icon="special://home/addons/plugin.video.emby/icon.png")
elif messageType == "UserConfigurationUpdated":
# Update user data set in userclient
@ -262,7 +254,7 @@ class WebSocket_Client(threading.Thread):
self.librarySync.refresh_views = True
def on_close(self, ws):
self.logMsg("Closed.", 2)
log("Closed.", 2)
def on_open(self, ws):
self.doUtils.postCapabilities(self.deviceId)
@ -272,11 +264,10 @@ class WebSocket_Client(threading.Thread):
# Server is offline
pass
else:
self.logMsg("Error: %s" % error, 2)
log("Error: %s" % error, 2)
def run(self):
window = utils.window
loglevel = int(window('emby_logLevel'))
# websocket.enableTrace(True)
@ -290,7 +281,7 @@ class WebSocket_Client(threading.Thread):
server = server.replace('http', "ws")
websocket_url = "%s?api_key=%s&deviceId=%s" % (server, token, self.deviceId)
self.logMsg("websocket url: %s" % websocket_url, 1)
log("websocket url: %s" % websocket_url, 1)
self.client = websocket.WebSocketApp(websocket_url,
on_message=self.on_message,
@ -298,7 +289,7 @@ class WebSocket_Client(threading.Thread):
on_close=self.on_close)
self.client.on_open = self.on_open
self.logMsg("----===## Starting WebSocketClient ##===----", 0)
log("----===## Starting WebSocketClient ##===----", 0)
while not self.monitor.abortRequested():
@ -310,10 +301,10 @@ class WebSocket_Client(threading.Thread):
# Abort was requested, exit
break
self.logMsg("##===---- WebSocketClient Stopped ----===##", 0)
log("##===---- WebSocketClient Stopped ----===##", 0)
def stopClient(self):
self.stopWebsocket = True
self.client.close()
self.logMsg("Stopping thread.", 1)
log("Stopping thread.", 1)

View file

@ -16,9 +16,9 @@ import xbmcvfs
#################################################################################################
_addon = xbmcaddon.Addon(id='plugin.video.emby')
addon_path = _addon.getAddonInfo('path').decode('utf-8')
base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8')
sys.path.append(base_resource)
_addon_path = _addon.getAddonInfo('path').decode('utf-8')
_base_resource = xbmc.translatePath(os.path.join(_addon_path, 'resources', 'lib')).decode('utf-8')
sys.path.append(_base_resource)
#################################################################################################
@ -28,9 +28,9 @@ import initialsetup
import kodimonitor
import librarysync
import player
import utils
import videonodes
import websocket_client as wsc
from utils import Logging, window, settings, language as lang
#################################################################################################
@ -49,8 +49,8 @@ class Service():
def __init__(self):
log = self.logMsg
window = utils.window
global log
log = Logging(self.__class__.__name__).log
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
@ -58,15 +58,14 @@ class Service():
self.monitor = xbmc.Monitor()
window('emby_logLevel', value=str(logLevel))
window('emby_kodiProfile', value=xbmc.translatePath("special://profile"))
window('emby_pluginpath', value=utils.settings('useDirectPaths'))
window('emby_kodiProfile', value=xbmc.translatePath('special://profile'))
# Initial logging
log("======== START %s ========" % self.addonName, 0)
log("Platform: %s" % (self.clientInfo.getPlatform()), 0)
log("KODI Version: %s" % xbmc.getInfoLabel('System.BuildVersion'), 0)
log("%s Version: %s" % (self.addonName, self.clientInfo.getVersion()), 0)
log("Using plugin paths: %s" % (utils.settings('useDirectPaths') != "true"), 0)
log("Using plugin paths: %s" % (settings('useDirectPaths') == "0"), 0)
log("Log Level: %s" % logLevel, 0)
# Reset window props for profile switch
@ -86,22 +85,13 @@ class Service():
# Set the minimum database version
window('emby_minDBVersion', value="1.1.63")
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
def ServiceEntryPoint(self):
log = self.logMsg
window = utils.window
lang = utils.language
# Important: Threads depending on abortRequest will not trigger
# if profile switch happens more than once.
monitor = self.monitor
kodiProfile = xbmc.translatePath("special://profile")
kodiProfile = xbmc.translatePath('special://profile')
# Server auto-detect
initialsetup.InitialSetup().setup()
@ -119,7 +109,7 @@ class Service():
if window('emby_kodiProfile') != kodiProfile:
# Profile change happened, terminate this thread and others
log("Kodi profile was: %s and changed to: %s. Terminating old Emby thread."
% (kodiProfile, utils.window('emby_kodiProfile')), 1)
% (kodiProfile, window('emby_kodiProfile')), 1)
break
@ -167,7 +157,7 @@ class Service():
else:
# Start up events
self.warn_auth = True
if utils.settings('connectMsg') == "true" and self.welcome_msg:
if settings('connectMsg') == "true" and self.welcome_msg:
# Reset authentication warnings
self.welcome_msg = False
# Get additional users
@ -291,7 +281,7 @@ class Service():
log("======== STOP %s ========" % self.addonName, 0)
# Delay option
delay = int(utils.settings('startupDelay'))
delay = int(settings('startupDelay'))
xbmc.log("Delaying emby startup by: %s sec..." % delay)
if delay and xbmc.Monitor().waitForAbort(delay):