Centralized logging
This commit is contained in:
parent
f5404fa1c1
commit
7a0f69e014
9 changed files with 542 additions and 516 deletions
|
@ -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
|
||||
|
|
|
@ -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']:
|
||||
|
|
|
@ -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 = {
|
||||
|
||||
|
|
|
@ -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,"
|
||||
'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")
|
||||
"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,"
|
||||
'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")
|
||||
"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,"
|
||||
'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")
|
||||
"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 "
|
||||
log("Update user rating to emby for itemid: %s "
|
||||
"| like: %s | favourite: %s | deletelike: %s"
|
||||
% (itemid, like, favourite, deletelike), 1)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
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 = xbmcaddon.Addon(id='plugin.video.emby')
|
||||
|
||||
def language(stringid):
|
||||
# Central string retrieval
|
||||
string = xbmcaddon.Addon(id='plugin.video.emby').getLocalizedString(stringid) #returns unicode object
|
||||
if value is not None:
|
||||
addon.setSetting(setting, value)
|
||||
else: # returns unicode object
|
||||
return addon.getSetting(setting)
|
||||
|
||||
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()
|
||||
|
||||
|
@ -118,6 +155,9 @@ def getKodiMusicDBPath():
|
|||
% 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)
|
|
@ -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 = [
|
||||
|
||||
|
|
|
@ -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,7 +191,7 @@ class WebSocket_Client(threading.Thread):
|
|||
|
||||
header = arguments['Header']
|
||||
text = arguments['Text']
|
||||
xbmcgui.Dialog().notification(
|
||||
dialog.notification(
|
||||
heading=header,
|
||||
message=text,
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
|
@ -250,8 +242,8 @@ class WebSocket_Client(threading.Thread):
|
|||
xbmc.executebuiltin(action)
|
||||
|
||||
elif messageType == "ServerRestarting":
|
||||
if utils.settings('supressRestartMsg') == "true":
|
||||
xbmcgui.Dialog().notification(
|
||||
if settings('supressRestartMsg') == "true":
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
message=lang(33006),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png")
|
||||
|
@ -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)
|
34
service.py
34
service.py
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue