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 clientinfo
|
||||||
import utils
|
from utils import Logging, settings
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -13,17 +13,16 @@ import utils
|
||||||
class API():
|
class API():
|
||||||
|
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
# item is the api response
|
# item is the api response
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|
||||||
self.clientinfo = clientinfo.ClientInfo()
|
self.clientinfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientinfo.getAddonName()
|
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):
|
def getUserData(self):
|
||||||
# Default
|
# Default
|
||||||
|
@ -223,7 +222,7 @@ class API():
|
||||||
resume = 0
|
resume = 0
|
||||||
if resume_seconds:
|
if resume_seconds:
|
||||||
resume = round(float(resume_seconds), 6)
|
resume = round(float(resume_seconds), 6)
|
||||||
jumpback = int(utils.settings('resumeJumpBack'))
|
jumpback = int(settings('resumeJumpBack'))
|
||||||
if resume > jumpback:
|
if resume > jumpback:
|
||||||
# To avoid negative bookmark
|
# To avoid negative bookmark
|
||||||
resume = resume - jumpback
|
resume = resume - jumpback
|
||||||
|
|
|
@ -12,9 +12,9 @@ import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
|
||||||
import utils
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import image_cache_thread
|
import image_cache_thread
|
||||||
|
from utils import Logging, window, settings, kodiSQL
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -29,24 +29,25 @@ class Artwork():
|
||||||
imageCacheThreads = []
|
imageCacheThreads = []
|
||||||
imageCacheLimitThreads = 0
|
imageCacheLimitThreads = 0
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.clientinfo = clientinfo.ClientInfo()
|
self.clientinfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientinfo.getAddonName()
|
self.addonName = self.clientinfo.getAddonName()
|
||||||
|
|
||||||
self.enableTextureCache = utils.settings('enableTextureCache') == "true"
|
self.enableTextureCache = settings('enableTextureCache') == "true"
|
||||||
self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit"))
|
self.imageCacheLimitThreads = int(settings('imageCacheLimit'))
|
||||||
self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5)
|
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:
|
if not self.xbmc_port and self.enableTextureCache:
|
||||||
self.setKodiWebServerDetails()
|
self.setKodiWebServerDetails()
|
||||||
|
|
||||||
self.userId = utils.window('emby_currUser')
|
self.userId = window('emby_currUser')
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = window('emby_server%s' % self.userId)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def double_urlencode(self, text):
|
def double_urlencode(self, text):
|
||||||
|
@ -56,8 +57,8 @@ class Artwork():
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def single_urlencode(self, text):
|
def single_urlencode(self, text):
|
||||||
|
# urlencode needs a utf- string
|
||||||
text = urllib.urlencode({'blahblahblah':text.encode("utf-8")}) #urlencode needs a utf- string
|
text = urllib.urlencode({'blahblahblah':text.encode("utf-8")})
|
||||||
text = text[13:]
|
text = text[13:]
|
||||||
|
|
||||||
return text.decode("utf-8") #return the result again as unicode
|
return text.decode("utf-8") #return the result again as unicode
|
||||||
|
@ -167,102 +168,116 @@ class Artwork():
|
||||||
def FullTextureCacheSync(self):
|
def FullTextureCacheSync(self):
|
||||||
# This method will sync all Kodi artwork to textures13.db
|
# This method will sync all Kodi artwork to textures13.db
|
||||||
# and cache them locally. This takes diskspace!
|
# 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
|
return
|
||||||
|
|
||||||
self.logMsg("Doing Image Cache Sync", 1)
|
log("Doing Image Cache Sync", 1)
|
||||||
|
|
||||||
dialog = xbmcgui.DialogProgress()
|
pdialog = xbmcgui.DialogProgress()
|
||||||
dialog.create("Emby for Kodi", "Image Cache Sync")
|
pdialog.create("Emby for Kodi", "Image Cache Sync")
|
||||||
|
|
||||||
# ask to rest all existing or not
|
# ask to rest all existing or not
|
||||||
if xbmcgui.Dialog().yesno("Image Texture Cache", "Reset all existing cache data first?", ""):
|
if dialog.yesno("Image Texture Cache", "Reset all existing cache data first?"):
|
||||||
self.logMsg("Resetting all cache data first", 1)
|
log("Resetting all cache data first.", 1)
|
||||||
|
|
||||||
# Remove all existing textures first
|
# 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):
|
if xbmcvfs.exists(path):
|
||||||
allDirs, allFiles = xbmcvfs.listdir(path)
|
allDirs, allFiles = xbmcvfs.listdir(path)
|
||||||
for dir in allDirs:
|
for dir in allDirs:
|
||||||
allDirs, allFiles = xbmcvfs.listdir(path+dir)
|
allDirs, allFiles = xbmcvfs.listdir(path+dir)
|
||||||
for file in allFiles:
|
for file in allFiles:
|
||||||
if os.path.supports_unicode_filenames:
|
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:
|
else:
|
||||||
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
|
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
|
||||||
|
|
||||||
# remove all existing data from texture DB
|
# remove all existing data from texture DB
|
||||||
textureconnection = utils.kodiSQL('texture')
|
connection = kodiSQL('texture')
|
||||||
texturecursor = textureconnection.cursor()
|
cursor = connection.cursor()
|
||||||
texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||||
rows = texturecursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
for row in rows:
|
for row in rows:
|
||||||
tableName = row[0]
|
tableName = row[0]
|
||||||
if(tableName != "version"):
|
if tableName != "version":
|
||||||
texturecursor.execute("DELETE FROM " + tableName)
|
cursor.execute("DELETE FROM " + tableName)
|
||||||
textureconnection.commit()
|
connection.commit()
|
||||||
texturecursor.close()
|
cursor.close()
|
||||||
|
|
||||||
# Cache all entries in video DB
|
# Cache all entries in video DB
|
||||||
connection = utils.kodiSQL('video')
|
connection = kodiSQL('video')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
|
cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
|
||||||
result = cursor.fetchall()
|
result = cursor.fetchall()
|
||||||
total = len(result)
|
total = len(result)
|
||||||
count = 1
|
log("Image cache sync about to process %s images" % total, 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
|
|
||||||
cursor.close()
|
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
|
# Cache all entries in music DB
|
||||||
connection = utils.kodiSQL('music')
|
connection = kodiSQL('music')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("SELECT url FROM art")
|
cursor.execute("SELECT url FROM art")
|
||||||
result = cursor.fetchall()
|
result = cursor.fetchall()
|
||||||
total = len(result)
|
total = len(result)
|
||||||
count = 1
|
log("Image cache sync about to process %s images" % total, 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
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
|
count = 0
|
||||||
self.logMsg("Waiting for all threads to exit", 1)
|
for url in result:
|
||||||
while len(self.imageCacheThreads) > 0:
|
|
||||||
|
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:
|
for thread in self.imageCacheThreads:
|
||||||
if thread.isFinished:
|
if thread.isFinished:
|
||||||
self.imageCacheThreads.remove(thread)
|
self.imageCacheThreads.remove(thread)
|
||||||
dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
|
pdialog.update(100, "Waiting for all threads to exit: %s" % len(self.imageCacheThreads))
|
||||||
self.logMsg("Waiting for all threads to exit: " + str(len(self.imageCacheThreads)), 1)
|
log("Waiting for all threads to exit: %s" % len(self.imageCacheThreads), 1)
|
||||||
xbmc.sleep(500)
|
xbmc.sleep(500)
|
||||||
|
|
||||||
dialog.close()
|
pdialog.close()
|
||||||
|
|
||||||
def addWorkerImageCacheThread(self, urlToAdd):
|
def addWorkerImageCacheThread(self, url):
|
||||||
|
|
||||||
while(True):
|
while True:
|
||||||
# removed finished
|
# removed finished
|
||||||
for thread in self.imageCacheThreads:
|
for thread in self.imageCacheThreads:
|
||||||
if thread.isFinished:
|
if thread.isFinished:
|
||||||
self.imageCacheThreads.remove(thread)
|
self.imageCacheThreads.remove(thread)
|
||||||
|
|
||||||
# add a new thread or wait and retry if we hit our limit
|
# 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 = image_cache_thread.image_cache_thread()
|
||||||
newThread.setUrl(self.double_urlencode(urlToAdd))
|
newThread.setUrl(self.double_urlencode(urlToAdd))
|
||||||
newThread.setHost(self.xbmc_host, self.xbmc_port)
|
newThread.setHost(self.xbmc_host, self.xbmc_port)
|
||||||
|
@ -271,23 +286,21 @@ class Artwork():
|
||||||
self.imageCacheThreads.append(newThread)
|
self.imageCacheThreads.append(newThread)
|
||||||
return
|
return
|
||||||
else:
|
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)
|
xbmc.sleep(50)
|
||||||
|
|
||||||
|
def cacheTexture(self, url):
|
||||||
def CacheTexture(self, url):
|
|
||||||
# Cache a single image url to the texture cache
|
# Cache a single image url to the texture cache
|
||||||
if url and self.enableTextureCache:
|
if url and self.enableTextureCache:
|
||||||
self.logMsg("Processing: %s" % url, 2)
|
log("Processing: %s" % url, 2)
|
||||||
|
|
||||||
if(self.imageCacheLimitThreads == 0 or self.imageCacheLimitThreads == None):
|
if not self.imageCacheLimitThreads:
|
||||||
# Add image to texture cache by simply calling it at the http endpoint
|
# Add image to texture cache by simply calling it at the http endpoint
|
||||||
|
|
||||||
url = self.double_urlencode(url)
|
url = self.double_urlencode(url)
|
||||||
try: # Extreme short timeouts so we will have a exception.
|
try: # Extreme short timeouts so we will have a exception.
|
||||||
response = requests.head(
|
response = requests.head(
|
||||||
url=(
|
url=("http://%s:%s/image/image://%s"
|
||||||
"http://%s:%s/image/image://%s"
|
|
||||||
% (self.xbmc_host, self.xbmc_port, url)),
|
% (self.xbmc_host, self.xbmc_port, url)),
|
||||||
auth=(self.xbmc_username, self.xbmc_password),
|
auth=(self.xbmc_username, self.xbmc_password),
|
||||||
timeout=(0.01, 0.01))
|
timeout=(0.01, 0.01))
|
||||||
|
@ -298,7 +311,7 @@ class Artwork():
|
||||||
self.addWorkerImageCacheThread(url)
|
self.addWorkerImageCacheThread(url)
|
||||||
|
|
||||||
|
|
||||||
def addArtwork(self, artwork, kodiId, mediaType, cursor):
|
def addArtwork(self, artwork, kodi_id, media_type, cursor):
|
||||||
# Kodi conversion table
|
# Kodi conversion table
|
||||||
kodiart = {
|
kodiart = {
|
||||||
|
|
||||||
|
@ -329,7 +342,7 @@ class Artwork():
|
||||||
"AND media_type = ?",
|
"AND media_type = ?",
|
||||||
"AND type LIKE ?"
|
"AND type LIKE ?"
|
||||||
))
|
))
|
||||||
cursor.execute(query, (kodiId, mediaType, "fanart%",))
|
cursor.execute(query, (kodi_id, media_type, "fanart%",))
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
if len(rows) > backdropsNumber:
|
if len(rows) > backdropsNumber:
|
||||||
|
@ -341,16 +354,16 @@ class Artwork():
|
||||||
"AND media_type = ?",
|
"AND media_type = ?",
|
||||||
"AND type LIKE ?"
|
"AND type LIKE ?"
|
||||||
))
|
))
|
||||||
cursor.execute(query, (kodiId, mediaType, "fanart_",))
|
cursor.execute(query, (kodi_id, media_type, "fanart_",))
|
||||||
|
|
||||||
# Process backdrops and extra fanart
|
# Process backdrops and extra fanart
|
||||||
index = ""
|
index = ""
|
||||||
for backdrop in backdrops:
|
for backdrop in backdrops:
|
||||||
self.addOrUpdateArt(
|
self.addOrUpdateArt(
|
||||||
imageUrl=backdrop,
|
image_url=backdrop,
|
||||||
kodiId=kodiId,
|
kodi_id=kodi_id,
|
||||||
mediaType=mediaType,
|
media_type=media_type,
|
||||||
imageType="%s%s" % ("fanart", index),
|
image_type="%s%s" % ("fanart", index),
|
||||||
cursor=cursor)
|
cursor=cursor)
|
||||||
|
|
||||||
if backdropsNumber > 1:
|
if backdropsNumber > 1:
|
||||||
|
@ -363,24 +376,24 @@ class Artwork():
|
||||||
# Primary art is processed as thumb and poster for Kodi.
|
# Primary art is processed as thumb and poster for Kodi.
|
||||||
for artType in kodiart[art]:
|
for artType in kodiart[art]:
|
||||||
self.addOrUpdateArt(
|
self.addOrUpdateArt(
|
||||||
imageUrl=artwork[art],
|
image_url=artwork[art],
|
||||||
kodiId=kodiId,
|
kodi_id=kodi_id,
|
||||||
mediaType=mediaType,
|
media_type=media_type,
|
||||||
imageType=artType,
|
image_type=artType,
|
||||||
cursor=cursor)
|
cursor=cursor)
|
||||||
|
|
||||||
elif kodiart.get(art):
|
elif kodiart.get(art):
|
||||||
# Process the rest artwork type that Kodi can use
|
# Process the rest artwork type that Kodi can use
|
||||||
self.addOrUpdateArt(
|
self.addOrUpdateArt(
|
||||||
imageUrl=artwork[art],
|
image_url=artwork[art],
|
||||||
kodiId=kodiId,
|
kodi_id=kodi_id,
|
||||||
mediaType=mediaType,
|
media_type=media_type,
|
||||||
imageType=kodiart[art],
|
image_type=kodiart[art],
|
||||||
cursor=cursor)
|
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
|
# Possible that the imageurl is an empty string
|
||||||
if imageUrl:
|
if image_url:
|
||||||
cacheimage = False
|
cacheimage = False
|
||||||
|
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
@ -391,13 +404,13 @@ class Artwork():
|
||||||
"AND media_type = ?",
|
"AND media_type = ?",
|
||||||
"AND type = ?"
|
"AND type = ?"
|
||||||
))
|
))
|
||||||
cursor.execute(query, (kodiId, mediaType, imageType,))
|
cursor.execute(query, (kodi_id, media_type, image_type,))
|
||||||
try: # Update the artwork
|
try: # Update the artwork
|
||||||
url = cursor.fetchone()[0]
|
url = cursor.fetchone()[0]
|
||||||
|
|
||||||
except TypeError: # Add the artwork
|
except TypeError: # Add the artwork
|
||||||
cacheimage = True
|
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 = (
|
query = (
|
||||||
'''
|
'''
|
||||||
|
@ -406,21 +419,20 @@ class Artwork():
|
||||||
VALUES (?, ?, ?, ?)
|
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
|
else: # Only cache artwork if it changed
|
||||||
if url != imageUrl:
|
if url != image_url:
|
||||||
cacheimage = True
|
cacheimage = True
|
||||||
|
|
||||||
# Only for the main backdrop, poster
|
# Only for the main backdrop, poster
|
||||||
if (utils.window('emby_initialScan') != "true" and
|
if (window('emby_initialScan') != "true" and
|
||||||
imageType in ("fanart", "poster")):
|
imageType in ("fanart", "poster")):
|
||||||
# Delete current entry before updating with the new one
|
# Delete current entry before updating with the new one
|
||||||
self.deleteCachedArtwork(url)
|
self.deleteCachedArtwork(url)
|
||||||
|
|
||||||
self.logMsg(
|
log("Updating Art url for %s kodiId: %s (%s) -> (%s)"
|
||||||
"Updating Art url for %s kodiId: %s (%s) -> (%s)"
|
% (image_type, kodi_id, url, image_url), 1)
|
||||||
% (imageType, kodiId, url, imageUrl), 1)
|
|
||||||
|
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
|
@ -430,13 +442,13 @@ class Artwork():
|
||||||
"AND media_type = ?",
|
"AND media_type = ?",
|
||||||
"AND 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
|
# Cache fanart and poster in Kodi texture cache
|
||||||
if cacheimage and imageType in ("fanart", "poster"):
|
if cacheimage and image_type in ("fanart", "poster"):
|
||||||
self.CacheTexture(imageUrl)
|
self.cacheTexture(image_url)
|
||||||
|
|
||||||
def deleteArtwork(self, kodiid, mediatype, cursor):
|
def deleteArtwork(self, kodi_id, media_type, cursor):
|
||||||
|
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
|
@ -445,7 +457,7 @@ class Artwork():
|
||||||
"WHERE media_id = ?",
|
"WHERE media_id = ?",
|
||||||
"AND media_type = ?"
|
"AND media_type = ?"
|
||||||
))
|
))
|
||||||
cursor.execute(query, (kodiid, mediatype,))
|
cursor.execute(query, (kodi_id, media_type,))
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
|
||||||
|
@ -456,7 +468,7 @@ class Artwork():
|
||||||
|
|
||||||
def deleteCachedArtwork(self, url):
|
def deleteCachedArtwork(self, url):
|
||||||
# Only necessary to remove and apply a new backdrop or poster
|
# Only necessary to remove and apply a new backdrop or poster
|
||||||
connection = utils.kodiSQL('texture')
|
connection = kodiSQL('texture')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -464,21 +476,21 @@ class Artwork():
|
||||||
cachedurl = cursor.fetchone()[0]
|
cachedurl = cursor.fetchone()[0]
|
||||||
|
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("Could not find cached url.", 1)
|
log("Could not find cached url.", 1)
|
||||||
|
|
||||||
except OperationalError:
|
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
|
else: # Delete thumbnail as well as the entry
|
||||||
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cachedurl).decode('utf-8')
|
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)
|
xbmcvfs.delete(thumbnails)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
except OperationalError:
|
except OperationalError:
|
||||||
self.logMsg("Issue deleting url from cache. Skipping.", 2)
|
log("Issue deleting url from cache. Skipping.", 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
@ -501,13 +513,13 @@ class Artwork():
|
||||||
|
|
||||||
return people
|
return people
|
||||||
|
|
||||||
def getUserArtwork(self, itemid, itemtype):
|
def getUserArtwork(self, item_id, item_type):
|
||||||
# Load user information set by UserClient
|
# Load user information set by UserClient
|
||||||
image = ("%s/emby/Users/%s/Images/%s?Format=original"
|
image = ("%s/emby/Users/%s/Images/%s?Format=original"
|
||||||
% (self.server, itemid, itemtype))
|
% (self.server, item_id, item_type))
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def getAllArtwork(self, item, parentInfo=False):
|
def getAllArtwork(self, item, parent_artwork=False):
|
||||||
|
|
||||||
itemid = item['Id']
|
itemid = item['Id']
|
||||||
artworks = item['ImageTags']
|
artworks = item['ImageTags']
|
||||||
|
@ -517,10 +529,10 @@ class Artwork():
|
||||||
maxWidth = 10000
|
maxWidth = 10000
|
||||||
customquery = ""
|
customquery = ""
|
||||||
|
|
||||||
if utils.settings('compressArt') == "true":
|
if settings('compressArt') == "true":
|
||||||
customquery = "&Quality=90"
|
customquery = "&Quality=90"
|
||||||
|
|
||||||
if utils.settings('enableCoverArt') == "false":
|
if settings('enableCoverArt') == "false":
|
||||||
customquery += "&EnableImageEnhancers=false"
|
customquery += "&EnableImageEnhancers=false"
|
||||||
|
|
||||||
allartworks = {
|
allartworks = {
|
||||||
|
@ -554,7 +566,7 @@ class Artwork():
|
||||||
allartworks[art] = artwork
|
allartworks[art] = artwork
|
||||||
|
|
||||||
# Process parent items if the main item is missing artwork
|
# Process parent items if the main item is missing artwork
|
||||||
if parentInfo:
|
if parent_artwork:
|
||||||
|
|
||||||
# Process parent backdrops
|
# Process parent backdrops
|
||||||
if not allartworks['Backdrop']:
|
if not allartworks['Backdrop']:
|
||||||
|
|
|
@ -16,7 +16,7 @@ import downloadutils
|
||||||
import playutils as putils
|
import playutils as putils
|
||||||
import playlist
|
import playlist
|
||||||
import read_embyserver as embyserver
|
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):
|
def __init__(self, item):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.item = item
|
self.item = item
|
||||||
self.API = api.API(self.item)
|
self.API = api.API(self.item)
|
||||||
|
|
||||||
|
@ -33,28 +36,20 @@ class PlaybackUtils():
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||||
|
|
||||||
self.userid = utils.window('emby_currUser')
|
self.userid = window('emby_currUser')
|
||||||
self.server = utils.window('emby_server%s' % self.userid)
|
self.server = window('emby_server%s' % self.userid)
|
||||||
|
|
||||||
self.artwork = artwork.Artwork()
|
self.artwork = artwork.Artwork()
|
||||||
self.emby = embyserver.Read_EmbyServer()
|
self.emby = embyserver.Read_EmbyServer()
|
||||||
self.pl = playlist.Playlist()
|
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):
|
def play(self, itemid, dbid=None):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
settings = utils.settings
|
|
||||||
|
|
||||||
listitem = xbmcgui.ListItem()
|
listitem = xbmcgui.ListItem()
|
||||||
playutils = putils.PlayUtils(self.item)
|
playutils = putils.PlayUtils(self.item)
|
||||||
|
|
||||||
self.logMsg("Play called.", 1)
|
log("Play called.", 1)
|
||||||
playurl = playutils.getPlayUrl()
|
playurl = playutils.getPlayUrl()
|
||||||
if not playurl:
|
if not playurl:
|
||||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||||
|
@ -77,9 +72,9 @@ class PlaybackUtils():
|
||||||
introsPlaylist = False
|
introsPlaylist = False
|
||||||
dummyPlaylist = False
|
dummyPlaylist = False
|
||||||
|
|
||||||
self.logMsg("Playlist start position: %s" % startPos, 2)
|
log("Playlist start position: %s" % startPos, 2)
|
||||||
self.logMsg("Playlist plugin position: %s" % currentPosition, 2)
|
log("Playlist plugin position: %s" % currentPosition, 2)
|
||||||
self.logMsg("Playlist size: %s" % sizePlaylist, 2)
|
log("Playlist size: %s" % sizePlaylist, 2)
|
||||||
|
|
||||||
############### RESUME POINT ################
|
############### RESUME POINT ################
|
||||||
|
|
||||||
|
@ -91,12 +86,11 @@ class PlaybackUtils():
|
||||||
if not propertiesPlayback:
|
if not propertiesPlayback:
|
||||||
|
|
||||||
window('emby_playbackProps', value="true")
|
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
|
if not homeScreen and not seektime and window('emby_customPlaylist') != "true":
|
||||||
window('emby_customPlaylist') != "true"):
|
|
||||||
|
|
||||||
self.logMsg("Adding dummy file to playlist.", 2)
|
log("Adding dummy file to playlist.", 2)
|
||||||
dummyPlaylist = True
|
dummyPlaylist = True
|
||||||
playlist.add(playurl, listitem, index=startPos)
|
playlist.add(playurl, listitem, index=startPos)
|
||||||
# Remove the original item from playlist
|
# Remove the original item from playlist
|
||||||
|
@ -116,18 +110,18 @@ class PlaybackUtils():
|
||||||
getTrailers = True
|
getTrailers = True
|
||||||
|
|
||||||
if settings('askCinema') == "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:
|
if not resp:
|
||||||
# User selected to not play trailers
|
# User selected to not play trailers
|
||||||
getTrailers = False
|
getTrailers = False
|
||||||
self.logMsg("Skip trailers.", 1)
|
log("Skip trailers.", 1)
|
||||||
|
|
||||||
if getTrailers:
|
if getTrailers:
|
||||||
for intro in intros['Items']:
|
for intro in intros['Items']:
|
||||||
# The server randomly returns intros, process them.
|
# The server randomly returns intros, process them.
|
||||||
introListItem = xbmcgui.ListItem()
|
introListItem = xbmcgui.ListItem()
|
||||||
introPlayurl = putils.PlayUtils(intro).getPlayUrl()
|
introPlayurl = putils.PlayUtils(intro).getPlayUrl()
|
||||||
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
|
log("Adding Intro: %s" % introPlayurl, 1)
|
||||||
|
|
||||||
# Set listitem and properties for intros
|
# Set listitem and properties for intros
|
||||||
pbutils = PlaybackUtils(intro)
|
pbutils = PlaybackUtils(intro)
|
||||||
|
@ -143,7 +137,7 @@ class PlaybackUtils():
|
||||||
if homeScreen and not seektime and not sizePlaylist:
|
if homeScreen and not seektime and not sizePlaylist:
|
||||||
# Extend our current playlist with the actual item to play
|
# Extend our current playlist with the actual item to play
|
||||||
# only if there's no playlist first
|
# 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())
|
self.pl.addtoPlaylist(dbid, self.item['Type'].lower())
|
||||||
|
|
||||||
# Ensure that additional parts are played after the main item
|
# Ensure that additional parts are played after the main item
|
||||||
|
@ -160,7 +154,7 @@ class PlaybackUtils():
|
||||||
|
|
||||||
additionalListItem = xbmcgui.ListItem()
|
additionalListItem = xbmcgui.ListItem()
|
||||||
additionalPlayurl = putils.PlayUtils(part).getPlayUrl()
|
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
|
# Set listitem and properties for each additional parts
|
||||||
pbutils = PlaybackUtils(part)
|
pbutils = PlaybackUtils(part)
|
||||||
|
@ -174,13 +168,13 @@ class PlaybackUtils():
|
||||||
if dummyPlaylist:
|
if dummyPlaylist:
|
||||||
# Added a dummy file to the playlist,
|
# Added a dummy file to the playlist,
|
||||||
# because the first item is going to fail automatically.
|
# 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)
|
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||||
|
|
||||||
|
|
||||||
# We just skipped adding properties. Reset flag for next time.
|
# We just skipped adding properties. Reset flag for next time.
|
||||||
elif propertiesPlayback:
|
elif propertiesPlayback:
|
||||||
self.logMsg("Resetting properties playback flag.", 2)
|
log("Resetting properties playback flag.", 2)
|
||||||
window('emby_playbackProps', clear=True)
|
window('emby_playbackProps', clear=True)
|
||||||
|
|
||||||
#self.pl.verifyPlaylist()
|
#self.pl.verifyPlaylist()
|
||||||
|
@ -190,7 +184,7 @@ class PlaybackUtils():
|
||||||
if window('emby_%s.playmethod' % playurl) == "Transcode":
|
if window('emby_%s.playmethod' % playurl) == "Transcode":
|
||||||
# Filter ISO since Emby does not probe anymore
|
# Filter ISO since Emby does not probe anymore
|
||||||
if self.item.get('VideoType') == "Iso":
|
if self.item.get('VideoType') == "Iso":
|
||||||
self.logMsg("Skipping audio/subs prompt, ISO detected.", 1)
|
log("Skipping audio/subs prompt, ISO detected.", 1)
|
||||||
else:
|
else:
|
||||||
playurl = playutils.audioSubsPref(playurl, listitem)
|
playurl = playutils.audioSubsPref(playurl, listitem)
|
||||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||||
|
@ -201,23 +195,22 @@ class PlaybackUtils():
|
||||||
############### PLAYBACK ################
|
############### PLAYBACK ################
|
||||||
|
|
||||||
if homeScreen and seektime and window('emby_customPlaylist') != "true":
|
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)
|
self.setListItem(listitem)
|
||||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
||||||
|
|
||||||
elif ((introsPlaylist and window('emby_customPlaylist') == "true") or
|
elif ((introsPlaylist and window('emby_customPlaylist') == "true") or
|
||||||
(homeScreen and not sizePlaylist)):
|
(homeScreen and not sizePlaylist)):
|
||||||
# Playlist was created just now, play it.
|
# Playlist was created just now, play it.
|
||||||
self.logMsg("Play playlist.", 1)
|
log("Play playlist.", 1)
|
||||||
xbmc.Player().play(playlist, startpos=startPos)
|
xbmc.Player().play(playlist, startpos=startPos)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg("Play as a regular item.", 1)
|
log("Play as a regular item.", 1)
|
||||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
||||||
|
|
||||||
def setProperties(self, playurl, listitem):
|
def setProperties(self, playurl, listitem):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
# Set all properties necessary for plugin path playback
|
# Set all properties necessary for plugin path playback
|
||||||
itemid = self.item['Id']
|
itemid = self.item['Id']
|
||||||
itemtype = self.item['Type']
|
itemtype = self.item['Type']
|
||||||
|
@ -233,7 +226,7 @@ class PlaybackUtils():
|
||||||
window('%s.refreshid' % embyitem, value=itemid)
|
window('%s.refreshid' % embyitem, value=itemid)
|
||||||
|
|
||||||
# Append external subtitles to stream
|
# Append external subtitles to stream
|
||||||
playmethod = utils.window('%s.playmethod' % embyitem)
|
playmethod = window('%s.playmethod' % embyitem)
|
||||||
# Only for direct stream
|
# Only for direct stream
|
||||||
if playmethod in ("DirectStream"):
|
if playmethod in ("DirectStream"):
|
||||||
# Direct play automatically appends external
|
# Direct play automatically appends external
|
||||||
|
@ -272,13 +265,13 @@ class PlaybackUtils():
|
||||||
kodiindex += 1
|
kodiindex += 1
|
||||||
|
|
||||||
mapping = json.dumps(mapping)
|
mapping = json.dumps(mapping)
|
||||||
utils.window('emby_%s.indexMapping' % playurl, value=mapping)
|
window('emby_%s.indexMapping' % playurl, value=mapping)
|
||||||
|
|
||||||
return externalsubs
|
return externalsubs
|
||||||
|
|
||||||
def setArtwork(self, listItem):
|
def setArtwork(self, listItem):
|
||||||
# Set up item and item info
|
# 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
|
# Set artwork for listitem
|
||||||
arttypes = {
|
arttypes = {
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,22 @@
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
|
|
||||||
import utils
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import downloadutils
|
import downloadutils
|
||||||
|
from utils import Logging, window, settings, kodiSQL
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
class Read_EmbyServer():
|
class Read_EmbyServer():
|
||||||
|
|
||||||
limitIndex = int(utils.settings('limitindex'))
|
limitIndex = int(settings('limitindex'))
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
window = utils.window
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
|
@ -27,17 +28,11 @@ class Read_EmbyServer():
|
||||||
self.userId = window('emby_currUser')
|
self.userId = window('emby_currUser')
|
||||||
self.server = window('emby_server%s' % self.userId)
|
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):
|
def split_list(self, itemlist, size):
|
||||||
# Split up list in pieces of size. Will generate a list of lists
|
# 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)]
|
return [itemlist[i:i+size] for i in range(0, len(itemlist), size)]
|
||||||
|
|
||||||
|
|
||||||
def getItem(self, itemid):
|
def getItem(self, itemid):
|
||||||
# This will return the full item
|
# This will return the full item
|
||||||
item = {}
|
item = {}
|
||||||
|
@ -60,7 +55,8 @@ class Read_EmbyServer():
|
||||||
'Ids': ",".join(itemlist),
|
'Ids': ",".join(itemlist),
|
||||||
'Fields': "Etag"
|
'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:
|
if result:
|
||||||
items.extend(result['Items'])
|
items.extend(result['Items'])
|
||||||
|
|
||||||
|
@ -86,7 +82,8 @@ class Read_EmbyServer():
|
||||||
"MediaSources,VoteCount"
|
"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:
|
if result:
|
||||||
items.extend(result['Items'])
|
items.extend(result['Items'])
|
||||||
|
|
||||||
|
@ -96,14 +93,15 @@ class Read_EmbyServer():
|
||||||
# Returns ancestors using embyId
|
# Returns ancestors using embyId
|
||||||
viewId = None
|
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":
|
if view['Type'] == "CollectionFolder":
|
||||||
# Found view
|
# Found view
|
||||||
viewId = view['Id']
|
viewId = view['Id']
|
||||||
|
|
||||||
# Compare to view table in emby database
|
# Compare to view table in emby database
|
||||||
emby = utils.kodiSQL('emby')
|
emby = kodiSQL('emby')
|
||||||
cursor_emby = emby.cursor()
|
cursor_emby = emby.cursor()
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
|
@ -124,7 +122,8 @@ class Read_EmbyServer():
|
||||||
|
|
||||||
return [viewName, viewId, mediatype]
|
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 = {
|
params = {
|
||||||
|
|
||||||
'ParentId': parentid,
|
'ParentId': parentid,
|
||||||
|
@ -137,39 +136,54 @@ class Read_EmbyServer():
|
||||||
'SortBy': sortby,
|
'SortBy': sortby,
|
||||||
'SortOrder': sortorder,
|
'SortOrder': sortorder,
|
||||||
'Filters': filter,
|
'Filters': filter,
|
||||||
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
'Fields': (
|
||||||
|
|
||||||
|
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"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)
|
return self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||||
|
|
||||||
def getTvChannels(self):
|
def getTvChannels(self):
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
|
|
||||||
'EnableImages': True,
|
'EnableImages': True,
|
||||||
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
'Fields': (
|
||||||
|
|
||||||
|
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"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):
|
def getTvRecordings(self, groupid):
|
||||||
if groupid == "root": groupid = ""
|
|
||||||
|
if groupid == "root":
|
||||||
|
groupid = ""
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
|
|
||||||
'GroupId': groupid,
|
'GroupId': groupid,
|
||||||
'EnableImages': True,
|
'EnableImages': True,
|
||||||
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
'Fields': (
|
||||||
|
|
||||||
|
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"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):
|
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
|
||||||
|
|
||||||
|
@ -197,7 +211,7 @@ class Read_EmbyServer():
|
||||||
items['TotalRecordCount'] = total
|
items['TotalRecordCount'] = total
|
||||||
|
|
||||||
except TypeError: # Failed to retrieve
|
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:
|
else:
|
||||||
index = 0
|
index = 0
|
||||||
|
@ -239,27 +253,27 @@ class Read_EmbyServer():
|
||||||
# Something happened to the connection
|
# Something happened to the connection
|
||||||
if not throttled:
|
if not throttled:
|
||||||
throttled = True
|
throttled = True
|
||||||
self.logMsg("Throttle activated.", 1)
|
log("Throttle activated.", 1)
|
||||||
|
|
||||||
if jump == highestjump:
|
if jump == highestjump:
|
||||||
# We already tried with the highestjump, but it failed. Reset value.
|
# We already tried with the highestjump, but it failed. Reset value.
|
||||||
self.logMsg("Reset highest value.", 1)
|
log("Reset highest value.", 1)
|
||||||
highestjump = 0
|
highestjump = 0
|
||||||
|
|
||||||
# Lower the number by half
|
# Lower the number by half
|
||||||
if highestjump:
|
if highestjump:
|
||||||
throttled = False
|
throttled = False
|
||||||
jump = highestjump
|
jump = highestjump
|
||||||
self.logMsg("Throttle deactivated.", 1)
|
log("Throttle deactivated.", 1)
|
||||||
else:
|
else:
|
||||||
jump = int(jump/4)
|
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
|
retry = 0
|
||||||
while utils.window('emby_online') != "true":
|
while window('emby_online') != "true":
|
||||||
# Wait server to come back online
|
# Wait server to come back online
|
||||||
if retry == 5:
|
if retry == 5:
|
||||||
self.logMsg("Unable to reconnect to server. Abort process.", 1)
|
log("Unable to reconnect to server. Abort process.", 1)
|
||||||
return items
|
return items
|
||||||
|
|
||||||
retry += 1
|
retry += 1
|
||||||
|
@ -287,7 +301,7 @@ class Read_EmbyServer():
|
||||||
increment = 10
|
increment = 10
|
||||||
|
|
||||||
jump += increment
|
jump += increment
|
||||||
self.logMsg("Increase jump limit to: %s" % jump, 1)
|
log("Increase jump limit to: %s" % jump, 1)
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def getViews(self, mediatype="", root=False, sortedlist=False):
|
def getViews(self, mediatype="", root=False, sortedlist=False):
|
||||||
|
@ -304,7 +318,7 @@ class Read_EmbyServer():
|
||||||
try:
|
try:
|
||||||
items = result['Items']
|
items = result['Items']
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("Error retrieving views for type: %s" % mediatype, 2)
|
log("Error retrieving views for type: %s" % mediatype, 2)
|
||||||
else:
|
else:
|
||||||
for item in items:
|
for item in items:
|
||||||
|
|
||||||
|
@ -373,15 +387,18 @@ class Read_EmbyServer():
|
||||||
return belongs
|
return belongs
|
||||||
|
|
||||||
def getMovies(self, parentId, basic=False, dialog=None):
|
def getMovies(self, parentId, basic=False, dialog=None):
|
||||||
|
|
||||||
return self.getSection(parentId, "Movie", basic=basic, dialog=dialog)
|
return self.getSection(parentId, "Movie", basic=basic, dialog=dialog)
|
||||||
|
|
||||||
def getBoxset(self, dialog=None):
|
def getBoxset(self, dialog=None):
|
||||||
|
|
||||||
return self.getSection(None, "BoxSet", dialog=dialog)
|
return self.getSection(None, "BoxSet", dialog=dialog)
|
||||||
|
|
||||||
def getMovies_byBoxset(self, boxsetid):
|
def getMovies_byBoxset(self, boxsetid):
|
||||||
return self.getSection(boxsetid, "Movie")
|
return self.getSection(boxsetid, "Movie")
|
||||||
|
|
||||||
def getMusicVideos(self, parentId, basic=False, dialog=None):
|
def getMusicVideos(self, parentId, basic=False, dialog=None):
|
||||||
|
|
||||||
return self.getSection(parentId, "MusicVideo", basic=basic, dialog=dialog)
|
return self.getSection(parentId, "MusicVideo", basic=basic, dialog=dialog)
|
||||||
|
|
||||||
def getHomeVideos(self, parentId):
|
def getHomeVideos(self, parentId):
|
||||||
|
@ -389,6 +406,7 @@ class Read_EmbyServer():
|
||||||
return self.getSection(parentId, "Video")
|
return self.getSection(parentId, "Video")
|
||||||
|
|
||||||
def getShows(self, parentId, basic=False, dialog=None):
|
def getShows(self, parentId, basic=False, dialog=None):
|
||||||
|
|
||||||
return self.getSection(parentId, "Series", basic=basic, dialog=dialog)
|
return self.getSection(parentId, "Series", basic=basic, dialog=dialog)
|
||||||
|
|
||||||
def getSeasons(self, showId):
|
def getSeasons(self, showId):
|
||||||
|
@ -404,7 +422,8 @@ class Read_EmbyServer():
|
||||||
'IsVirtualUnaired': False,
|
'IsVirtualUnaired': False,
|
||||||
'Fields': "Etag"
|
'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:
|
if result:
|
||||||
items = result
|
items = result
|
||||||
|
|
||||||
|
@ -422,7 +441,6 @@ class Read_EmbyServer():
|
||||||
|
|
||||||
return self.getSection(seasonId, "Episode")
|
return self.getSection(seasonId, "Episode")
|
||||||
|
|
||||||
|
|
||||||
def getArtists(self, dialog=None):
|
def getArtists(self, dialog=None):
|
||||||
|
|
||||||
items = {
|
items = {
|
||||||
|
@ -444,7 +462,7 @@ class Read_EmbyServer():
|
||||||
items['TotalRecordCount'] = total
|
items['TotalRecordCount'] = total
|
||||||
|
|
||||||
except TypeError: # Failed to retrieve
|
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:
|
else:
|
||||||
index = 1
|
index = 1
|
||||||
|
@ -478,17 +496,20 @@ class Read_EmbyServer():
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def getAlbums(self, basic=False, dialog=None):
|
def getAlbums(self, basic=False, dialog=None):
|
||||||
|
|
||||||
return self.getSection(None, "MusicAlbum", sortby="DateCreated", basic=basic, dialog=dialog)
|
return self.getSection(None, "MusicAlbum", sortby="DateCreated", basic=basic, dialog=dialog)
|
||||||
|
|
||||||
def getAlbumsbyArtist(self, artistId):
|
def getAlbumsbyArtist(self, artistId):
|
||||||
|
|
||||||
return self.getSection(artistId, "MusicAlbum", sortby="DateCreated")
|
return self.getSection(artistId, "MusicAlbum", sortby="DateCreated")
|
||||||
|
|
||||||
def getSongs(self, basic=False, dialog=None):
|
def getSongs(self, basic=False, dialog=None):
|
||||||
|
|
||||||
return self.getSection(None, "Audio", basic=basic, dialog=dialog)
|
return self.getSection(None, "Audio", basic=basic, dialog=dialog)
|
||||||
|
|
||||||
def getSongsbyAlbum(self, albumId):
|
def getSongsbyAlbum(self, albumId):
|
||||||
return self.getSection(albumId, "Audio")
|
|
||||||
|
|
||||||
|
return self.getSection(albumId, "Audio")
|
||||||
|
|
||||||
def getAdditionalParts(self, itemId):
|
def getAdditionalParts(self, itemId):
|
||||||
|
|
||||||
|
@ -497,8 +518,8 @@ class Read_EmbyServer():
|
||||||
'Items': [],
|
'Items': [],
|
||||||
'TotalRecordCount': 0
|
'TotalRecordCount': 0
|
||||||
}
|
}
|
||||||
|
url = "{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId
|
||||||
result = self.doUtils("{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId)
|
result = self.doUtils(url)
|
||||||
if result:
|
if result:
|
||||||
items = result
|
items = result
|
||||||
|
|
||||||
|
@ -520,21 +541,27 @@ class Read_EmbyServer():
|
||||||
|
|
||||||
def updateUserRating(self, itemid, like=None, favourite=None, deletelike=False):
|
def updateUserRating(self, itemid, like=None, favourite=None, deletelike=False):
|
||||||
# Updates the user rating to Emby
|
# Updates the user rating to Emby
|
||||||
|
doUtils = self.doUtils
|
||||||
|
|
||||||
if favourite:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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"
|
"| like: %s | favourite: %s | deletelike: %s"
|
||||||
% (itemid, like, favourite, deletelike), 1)
|
% (itemid, like, favourite, deletelike), 1)
|
|
@ -11,9 +11,9 @@ import xbmcaddon
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
|
||||||
import artwork
|
import artwork
|
||||||
import utils
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import downloadutils
|
import downloadutils
|
||||||
|
from utils import Logging, window, settings, language as lang
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -39,6 +39,9 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
self.addon = xbmcaddon.Addon()
|
self.addon = xbmcaddon.Addon()
|
||||||
|
|
||||||
|
@ -47,25 +50,20 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
|
||||||
|
|
||||||
className = self.__class__.__name__
|
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
|
||||||
|
|
||||||
|
|
||||||
def getAdditionalUsers(self):
|
def getAdditionalUsers(self):
|
||||||
|
|
||||||
additionalUsers = utils.settings('additionalUsers')
|
additionalUsers = settings('additionalUsers')
|
||||||
|
|
||||||
if additionalUsers:
|
if additionalUsers:
|
||||||
self.AdditionalUser = additionalUsers.split(',')
|
self.AdditionalUser = additionalUsers.split(',')
|
||||||
|
|
||||||
def getUsername(self):
|
def getUsername(self):
|
||||||
|
|
||||||
username = utils.settings('username')
|
username = settings('username')
|
||||||
|
|
||||||
if not username:
|
if not username:
|
||||||
self.logMsg("No username saved.", 2)
|
log("No username saved.", 2)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return username
|
return username
|
||||||
|
@ -73,7 +71,7 @@ class UserClient(threading.Thread):
|
||||||
def getLogLevel(self):
|
def getLogLevel(self):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logLevel = int(utils.settings('logLevel'))
|
logLevel = int(settings('logLevel'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logLevel = 0
|
logLevel = 0
|
||||||
|
|
||||||
|
@ -81,9 +79,6 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def getUserId(self):
|
def getUserId(self):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
settings = utils.settings
|
|
||||||
|
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
w_userId = window('emby_currUser')
|
w_userId = window('emby_currUser')
|
||||||
s_userId = settings('userId%s' % username)
|
s_userId = settings('userId%s' % username)
|
||||||
|
@ -93,22 +88,20 @@ class UserClient(threading.Thread):
|
||||||
if not s_userId:
|
if not s_userId:
|
||||||
# Save access token if it's missing from settings
|
# Save access token if it's missing from settings
|
||||||
settings('userId%s' % username, value=w_userId)
|
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)
|
% (username, w_userId), 2)
|
||||||
return w_userId
|
return w_userId
|
||||||
# Verify the settings
|
# Verify the settings
|
||||||
elif s_userId:
|
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)
|
% (username, s_userId), 2)
|
||||||
return s_userId
|
return s_userId
|
||||||
# No userId found
|
# No userId found
|
||||||
else:
|
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):
|
def getServer(self, prefix=True):
|
||||||
|
|
||||||
settings = utils.settings
|
|
||||||
|
|
||||||
alternate = settings('altip') == "true"
|
alternate = settings('altip') == "true"
|
||||||
if alternate:
|
if alternate:
|
||||||
# Alternate host
|
# Alternate host
|
||||||
|
@ -124,7 +117,7 @@ class UserClient(threading.Thread):
|
||||||
server = host + ":" + port
|
server = host + ":" + port
|
||||||
|
|
||||||
if not host:
|
if not host:
|
||||||
self.logMsg("No server information saved.", 2)
|
log("No server information saved.", 2)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# If https is true
|
# If https is true
|
||||||
|
@ -141,9 +134,6 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def getToken(self):
|
def getToken(self):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
settings = utils.settings
|
|
||||||
|
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
userId = self.getUserId()
|
userId = self.getUserId()
|
||||||
w_token = window('emby_accessToken%s' % userId)
|
w_token = window('emby_accessToken%s' % userId)
|
||||||
|
@ -154,23 +144,21 @@ class UserClient(threading.Thread):
|
||||||
if not s_token:
|
if not s_token:
|
||||||
# Save access token if it's missing from settings
|
# Save access token if it's missing from settings
|
||||||
settings('accessToken', value=w_token)
|
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)
|
% (username, w_token), 2)
|
||||||
return w_token
|
return w_token
|
||||||
# Verify the settings
|
# Verify the settings
|
||||||
elif s_token:
|
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)
|
% (username, s_token), 2)
|
||||||
window('emby_accessToken%s' % username, value=s_token)
|
window('emby_accessToken%s' % username, value=s_token)
|
||||||
return s_token
|
return s_token
|
||||||
else:
|
else:
|
||||||
self.logMsg("No token found.", 1)
|
log("No token found.", 1)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def getSSLverify(self):
|
def getSSLverify(self):
|
||||||
# Verify host certificate
|
# Verify host certificate
|
||||||
settings = utils.settings
|
|
||||||
|
|
||||||
s_sslverify = settings('sslverify')
|
s_sslverify = settings('sslverify')
|
||||||
if settings('altip') == "true":
|
if settings('altip') == "true":
|
||||||
s_sslverify = settings('secondsslverify')
|
s_sslverify = settings('secondsslverify')
|
||||||
|
@ -182,8 +170,6 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def getSSL(self):
|
def getSSL(self):
|
||||||
# Client side certificate
|
# Client side certificate
|
||||||
settings = utils.settings
|
|
||||||
|
|
||||||
s_cert = settings('sslcert')
|
s_cert = settings('sslcert')
|
||||||
if settings('altip') == "true":
|
if settings('altip') == "true":
|
||||||
s_cert = settings('secondsslcert')
|
s_cert = settings('secondsslcert')
|
||||||
|
@ -201,16 +187,16 @@ class UserClient(threading.Thread):
|
||||||
self.userSettings = result
|
self.userSettings = result
|
||||||
# Set user image for skin display
|
# Set user image for skin display
|
||||||
if result.get('PrimaryImageTag'):
|
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
|
# Set resume point max
|
||||||
result = doUtils("{server}/emby/System/Configuration?format=json")
|
result = doUtils("{server}/emby/System/Configuration?format=json")
|
||||||
|
settings('markPlayed', value=str(result['MaxResumePct']))
|
||||||
utils.settings('markPlayed', value=str(result['MaxResumePct']))
|
|
||||||
|
|
||||||
def getPublicUsers(self):
|
def getPublicUsers(self):
|
||||||
# Get public Users
|
# 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 != "":
|
if result != "":
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
|
@ -220,13 +206,11 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def hasAccess(self):
|
def hasAccess(self):
|
||||||
# hasAccess is verified in service.py
|
# hasAccess is verified in service.py
|
||||||
window = utils.window
|
|
||||||
|
|
||||||
result = self.doUtils.downloadUrl("{server}/emby/Users?format=json")
|
result = self.doUtils.downloadUrl("{server}/emby/Users?format=json")
|
||||||
|
|
||||||
if result == False:
|
if result == False:
|
||||||
# Access is restricted, set in downloadutils.py via exception
|
# Access is restricted, set in downloadutils.py via exception
|
||||||
self.logMsg("Access is restricted.", 1)
|
log("Access is restricted.", 1)
|
||||||
self.HasAccess = False
|
self.HasAccess = False
|
||||||
|
|
||||||
elif window('emby_online') != "true":
|
elif window('emby_online') != "true":
|
||||||
|
@ -234,15 +218,13 @@ class UserClient(threading.Thread):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
elif window('emby_serverStatus') == "restricted":
|
elif window('emby_serverStatus') == "restricted":
|
||||||
self.logMsg("Access is granted.", 1)
|
log("Access is granted.", 1)
|
||||||
self.HasAccess = True
|
self.HasAccess = True
|
||||||
window('emby_serverStatus', clear=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):
|
def loadCurrUser(self, authenticated=False):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
|
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
userId = self.getUserId()
|
userId = self.getUserId()
|
||||||
|
@ -290,9 +272,6 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def authenticate(self):
|
def authenticate(self):
|
||||||
|
|
||||||
lang = utils.language
|
|
||||||
window = utils.window
|
|
||||||
settings = utils.settings
|
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
||||||
# Get /profile/addon_data
|
# Get /profile/addon_data
|
||||||
|
@ -304,12 +283,12 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
# If there's no settings.xml
|
# If there's no settings.xml
|
||||||
if not hasSettings:
|
if not hasSettings:
|
||||||
self.logMsg("No settings.xml found.", 1)
|
log("No settings.xml found.", 1)
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return
|
||||||
# If no user information
|
# If no user information
|
||||||
elif not server or not username:
|
elif not server or not username:
|
||||||
self.logMsg("Missing server information.", 1)
|
log("Missing server information.", 1)
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return
|
||||||
# If there's a token, load the user
|
# If there's a token, load the user
|
||||||
|
@ -319,9 +298,9 @@ class UserClient(threading.Thread):
|
||||||
if result is False:
|
if result is False:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.logMsg("Current user: %s" % self.currUser, 1)
|
log("Current user: %s" % self.currUser, 1)
|
||||||
self.logMsg("Current userId: %s" % self.currUserId, 1)
|
log("Current userId: %s" % self.currUserId, 1)
|
||||||
self.logMsg("Current accessToken: %s" % self.currToken, 2)
|
log("Current accessToken: %s" % self.currToken, 2)
|
||||||
return
|
return
|
||||||
|
|
||||||
##### AUTHENTICATE USER #####
|
##### AUTHENTICATE USER #####
|
||||||
|
@ -341,7 +320,7 @@ class UserClient(threading.Thread):
|
||||||
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
||||||
# If password dialog is cancelled
|
# If password dialog is cancelled
|
||||||
if not password:
|
if not password:
|
||||||
self.logMsg("No password entered.", 0)
|
log("No password entered.", 0)
|
||||||
window('emby_serverStatus', value="Stop")
|
window('emby_serverStatus', value="Stop")
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return
|
||||||
|
@ -356,16 +335,17 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
# Authenticate username and password
|
# Authenticate username and password
|
||||||
data = {'username': username, 'password': sha1}
|
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:
|
try:
|
||||||
self.logMsg("Auth response: %s" % result, 1)
|
log("Auth response: %s" % result, 1)
|
||||||
accessToken = result['AccessToken']
|
accessToken = result['AccessToken']
|
||||||
|
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Failed to retrieve the api key.", 1)
|
log("Failed to retrieve the api key.", 1)
|
||||||
accessToken = None
|
accessToken = None
|
||||||
|
|
||||||
if accessToken is not None:
|
if accessToken is not None:
|
||||||
|
@ -374,19 +354,19 @@ class UserClient(threading.Thread):
|
||||||
"%s %s!" % (lang(33000), self.currUser.decode('utf-8')))
|
"%s %s!" % (lang(33000), self.currUser.decode('utf-8')))
|
||||||
settings('accessToken', value=accessToken)
|
settings('accessToken', value=accessToken)
|
||||||
settings('userId%s' % username, value=result['User']['Id'])
|
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)
|
self.loadCurrUser(authenticated=True)
|
||||||
window('emby_serverStatus', clear=True)
|
window('emby_serverStatus', clear=True)
|
||||||
self.retry = 0
|
self.retry = 0
|
||||||
else:
|
else:
|
||||||
self.logMsg("User authentication failed.", 1)
|
log("User authentication failed.", 1)
|
||||||
settings('accessToken', value="")
|
settings('accessToken', value="")
|
||||||
settings('userId%s' % username, value="")
|
settings('userId%s' % username, value="")
|
||||||
dialog.ok(lang(33001), lang(33009))
|
dialog.ok(lang(33001), lang(33009))
|
||||||
|
|
||||||
# Give two attempts at entering password
|
# Give two attempts at entering password
|
||||||
if self.retry == 2:
|
if self.retry == 2:
|
||||||
self.logMsg("Too many retries. "
|
log("Too many retries. "
|
||||||
"You can retry by resetting attempts in the addon settings.", 1)
|
"You can retry by resetting attempts in the addon settings.", 1)
|
||||||
window('emby_serverStatus', value="Stop")
|
window('emby_serverStatus', value="Stop")
|
||||||
dialog.ok(lang(33001), lang(33010))
|
dialog.ok(lang(33001), lang(33010))
|
||||||
|
@ -396,23 +376,21 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def resetClient(self):
|
def resetClient(self):
|
||||||
|
|
||||||
self.logMsg("Reset UserClient authentication.", 1)
|
log("Reset UserClient authentication.", 1)
|
||||||
if self.currToken is not None:
|
if self.currToken is not None:
|
||||||
# In case of 401, removed saved token
|
# In case of 401, removed saved token
|
||||||
utils.settings('accessToken', value="")
|
settings('accessToken', value="")
|
||||||
utils.window('emby_accessToken%s' % self.getUserId(), clear=True)
|
window('emby_accessToken%s' % self.getUserId(), clear=True)
|
||||||
self.currToken = None
|
self.currToken = None
|
||||||
self.logMsg("User token has been removed.", 1)
|
log("User token has been removed.", 1)
|
||||||
|
|
||||||
self.auth = True
|
self.auth = True
|
||||||
self.currUser = None
|
self.currUser = None
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
|
|
||||||
monitor = xbmc.Monitor()
|
monitor = xbmc.Monitor()
|
||||||
self.logMsg("----===## Starting UserClient ##===----", 0)
|
log("----===## Starting UserClient ##===----", 0)
|
||||||
|
|
||||||
while not monitor.abortRequested():
|
while not monitor.abortRequested():
|
||||||
|
|
||||||
|
@ -447,8 +425,8 @@ class UserClient(threading.Thread):
|
||||||
# The status Stop is for when user cancelled password dialog.
|
# The status Stop is for when user cancelled password dialog.
|
||||||
if server and username and status != "Stop":
|
if server and username and status != "Stop":
|
||||||
# Only if there's information found to login
|
# Only if there's information found to login
|
||||||
self.logMsg("Server found: %s" % server, 2)
|
log("Server found: %s" % server, 2)
|
||||||
self.logMsg("Username found: %s" % username, 2)
|
log("Username found: %s" % username, 2)
|
||||||
self.auth = True
|
self.auth = True
|
||||||
|
|
||||||
|
|
||||||
|
@ -461,7 +439,7 @@ class UserClient(threading.Thread):
|
||||||
break
|
break
|
||||||
|
|
||||||
self.doUtils.stopSession()
|
self.doUtils.stopSession()
|
||||||
self.logMsg("##===---- UserClient Stopped ----===##", 0)
|
log("##===---- UserClient Stopped ----===##", 0)
|
||||||
|
|
||||||
def stopClient(self):
|
def stopClient(self):
|
||||||
# When emby for kodi terminates
|
# When emby for kodi terminates
|
||||||
|
|
|
@ -20,7 +20,7 @@ import xbmcgui
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
# Main methods
|
||||||
|
|
||||||
def logMsg(title, msg, level=1):
|
def logMsg(title, msg, level=1):
|
||||||
|
|
||||||
|
@ -43,43 +43,80 @@ def logMsg(title, msg, level=1):
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
xbmc.log("%s -> %s" % (title, msg.encode('utf-8')))
|
xbmc.log("%s -> %s" % (title, msg.encode('utf-8')))
|
||||||
|
|
||||||
def window(property, value=None, clear=False, windowid=10000):
|
class Logging():
|
||||||
# Get or set window property
|
|
||||||
WINDOW = xbmcgui.Window(windowid)
|
|
||||||
|
|
||||||
#setproperty accepts both string and unicode but utf-8 strings are adviced by kodi devs because some unicode can give issues
|
LOGGINGCLASS = None
|
||||||
'''if isinstance(property, unicode):
|
|
||||||
property = property.encode("utf-8")
|
|
||||||
if isinstance(value, unicode):
|
def __init__(self, classname=""):
|
||||||
value = value.encode("utf-8")'''
|
|
||||||
|
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:
|
if clear:
|
||||||
WINDOW.clearProperty(property)
|
WINDOW.clearProperty(property)
|
||||||
elif value is not None:
|
elif value is not None:
|
||||||
WINDOW.setProperty(property, value)
|
WINDOW.setProperty(property, value)
|
||||||
else: #getproperty returns string so convert to unicode
|
else:
|
||||||
return WINDOW.getProperty(property)#.decode("utf-8")
|
return WINDOW.getProperty(property)
|
||||||
|
|
||||||
def settings(setting, value=None):
|
def settings(setting, value=None):
|
||||||
# Get or add addon setting
|
# Get or add addon setting
|
||||||
if value is not None:
|
addon = xbmcaddon.Addon(id='plugin.video.emby')
|
||||||
xbmcaddon.Addon(id='plugin.video.emby').setSetting(setting, value)
|
|
||||||
else:
|
|
||||||
return xbmcaddon.Addon(id='plugin.video.emby').getSetting(setting) #returns unicode object
|
|
||||||
|
|
||||||
def language(stringid):
|
if value is not None:
|
||||||
# Central string retrieval
|
addon.setSetting(setting, value)
|
||||||
string = xbmcaddon.Addon(id='plugin.video.emby').getLocalizedString(stringid) #returns unicode object
|
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
|
return string
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
# Database related methods
|
||||||
|
|
||||||
def kodiSQL(media_type="video"):
|
def kodiSQL(media_type="video"):
|
||||||
|
|
||||||
if media_type == "emby":
|
if media_type == "emby":
|
||||||
dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8')
|
dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8')
|
||||||
elif media_type == "music":
|
|
||||||
dbPath = getKodiMusicDBPath()
|
|
||||||
elif media_type == "texture":
|
elif media_type == "texture":
|
||||||
dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
|
dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
|
||||||
|
elif media_type == "music":
|
||||||
|
dbPath = getKodiMusicDBPath()
|
||||||
else:
|
else:
|
||||||
dbPath = getKodiVideoDBPath()
|
dbPath = getKodiVideoDBPath()
|
||||||
|
|
||||||
|
@ -118,6 +155,9 @@ def getKodiMusicDBPath():
|
||||||
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
|
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
|
||||||
return dbPath
|
return dbPath
|
||||||
|
|
||||||
|
#################################################################################################
|
||||||
|
# Utility methods
|
||||||
|
|
||||||
def getScreensaver():
|
def getScreensaver():
|
||||||
# Get the current screensaver value
|
# Get the current screensaver value
|
||||||
query = {
|
query = {
|
||||||
|
@ -145,139 +185,8 @@ def setScreensaver(value):
|
||||||
'value': value
|
'value': value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logMsg("EMBY", "Toggling screensaver: %s %s" % (value, xbmc.executeJSONRPC(json.dumps(query))), 1)
|
result = xbmc.executeJSONRPC(json.dumps(query))
|
||||||
|
log("Toggling screensaver: %s %s" % (value, result), 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
|
|
||||||
|
|
||||||
def convertdate(date):
|
def convertdate(date):
|
||||||
try:
|
try:
|
||||||
|
@ -344,6 +253,141 @@ def indent(elem, level=0):
|
||||||
if level and (not elem.tail or not elem.tail.strip()):
|
if level and (not elem.tail or not elem.tail.strip()):
|
||||||
elem.tail = i
|
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():
|
def sourcesXML():
|
||||||
# To make Master lock compatible
|
# To make Master lock compatible
|
||||||
path = xbmc.translatePath("special://profile/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/").decode('utf-8')
|
||||||
|
@ -413,12 +457,11 @@ def passwordsXML():
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if path.find('.//from').text == "smb://%s/" % credentials:
|
if path.find('.//from').text == "smb://%s/" % credentials:
|
||||||
paths.remove(path)
|
paths.remove(path)
|
||||||
logMsg("EMBY", "Successfully removed credentials for: %s"
|
log("Successfully removed credentials for: %s" % credentials, 1)
|
||||||
% credentials, 1)
|
|
||||||
etree.ElementTree(root).write(xmlpath)
|
etree.ElementTree(root).write(xmlpath)
|
||||||
break
|
break
|
||||||
else:
|
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="")
|
settings('networkCreds', value="")
|
||||||
xbmcgui.Dialog().notification(
|
xbmcgui.Dialog().notification(
|
||||||
|
@ -473,7 +516,7 @@ def passwordsXML():
|
||||||
|
|
||||||
# Add credentials
|
# Add credentials
|
||||||
settings('networkCreds', value="%s" % server)
|
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
|
# Prettify and write to file
|
||||||
try:
|
try:
|
||||||
indent(root)
|
indent(root)
|
||||||
|
@ -501,7 +544,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
|
|
||||||
# Create the playlist directory
|
# Create the playlist directory
|
||||||
if not xbmcvfs.exists(path):
|
if not xbmcvfs.exists(path):
|
||||||
logMsg("EMBY", "Creating directory: %s" % path, 1)
|
log("Creating directory: %s" % path, 1)
|
||||||
xbmcvfs.mkdirs(path)
|
xbmcvfs.mkdirs(path)
|
||||||
|
|
||||||
# Only add the playlist if it doesn't already exists
|
# Only add the playlist if it doesn't already exists
|
||||||
|
@ -509,7 +552,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
|
|
||||||
if delete:
|
if delete:
|
||||||
xbmcvfs.delete(xsppath)
|
xbmcvfs.delete(xsppath)
|
||||||
logMsg("EMBY", "Successfully removed playlist: %s." % tagname, 1)
|
log("Successfully removed playlist: %s." % tagname, 1)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -517,11 +560,11 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
itemtypes = {
|
itemtypes = {
|
||||||
'homevideos': "movies"
|
'homevideos': "movies"
|
||||||
}
|
}
|
||||||
logMsg("EMBY", "Writing playlist file to: %s" % xsppath, 1)
|
log("Writing playlist file to: %s" % xsppath, 1)
|
||||||
try:
|
try:
|
||||||
f = xbmcvfs.File(xsppath, 'w')
|
f = xbmcvfs.File(xsppath, 'w')
|
||||||
except:
|
except:
|
||||||
logMsg("EMBY", "Failed to create playlist: %s" % xsppath, 1)
|
log("Failed to create playlist: %s" % xsppath, 1)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
f.write(
|
f.write(
|
||||||
|
@ -535,7 +578,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
'</smartplaylist>'
|
'</smartplaylist>'
|
||||||
% (itemtypes.get(mediatype, mediatype), plname, tagname))
|
% (itemtypes.get(mediatype, mediatype), plname, tagname))
|
||||||
f.close()
|
f.close()
|
||||||
logMsg("EMBY", "Successfully added playlist: %s" % tagname)
|
log("Successfully added playlist: %s" % tagname, 1)
|
||||||
|
|
||||||
def deletePlaylists():
|
def deletePlaylists():
|
||||||
|
|
||||||
|
@ -557,10 +600,10 @@ def deleteNodes():
|
||||||
try:
|
try:
|
||||||
shutil.rmtree("%s%s" % (path, dir.decode('utf-8')))
|
shutil.rmtree("%s%s" % (path, dir.decode('utf-8')))
|
||||||
except:
|
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:
|
for file in files:
|
||||||
if file.decode('utf-8').startswith('emby'):
|
if file.decode('utf-8').startswith('emby'):
|
||||||
try:
|
try:
|
||||||
xbmcvfs.delete("%s%s" % (path, file.decode('utf-8')))
|
xbmcvfs.delete("%s%s" % (path, file.decode('utf-8')))
|
||||||
except:
|
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 clientinfo
|
||||||
import utils
|
import utils
|
||||||
|
from utils import Logging, window, language as lang
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -20,16 +21,14 @@ class VideoNodes(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
clientInfo = clientinfo.ClientInfo()
|
clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = clientInfo.getAddonName()
|
self.addonName = clientInfo.getAddonName()
|
||||||
|
|
||||||
self.kodiversion = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
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):
|
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):
|
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
|
|
||||||
if viewtype == "mixed":
|
if viewtype == "mixed":
|
||||||
dirname = "%s - %s" % (viewid, mediatype)
|
dirname = "%s - %s" % (viewid, mediatype)
|
||||||
else:
|
else:
|
||||||
|
@ -82,7 +79,7 @@ class VideoNodes(object):
|
||||||
for file in files:
|
for file in files:
|
||||||
xbmcvfs.delete(nodepath + file)
|
xbmcvfs.delete(nodepath + file)
|
||||||
|
|
||||||
self.logMsg("Sucessfully removed videonode: %s." % tagname, 1)
|
log("Sucessfully removed videonode: %s." % tagname, 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create index entry
|
# Create index entry
|
||||||
|
@ -184,7 +181,7 @@ class VideoNodes(object):
|
||||||
# Get label
|
# Get label
|
||||||
stringid = nodes[node]
|
stringid = nodes[node]
|
||||||
if node != "1":
|
if node != "1":
|
||||||
label = utils.language(stringid)
|
label = lang(stringid)
|
||||||
if not label:
|
if not label:
|
||||||
label = xbmc.getLocalizedString(stringid)
|
label = xbmc.getLocalizedString(stringid)
|
||||||
else:
|
else:
|
||||||
|
@ -319,8 +316,6 @@ class VideoNodes(object):
|
||||||
|
|
||||||
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
|
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
|
|
||||||
tagname = tagname.encode('utf-8')
|
tagname = tagname.encode('utf-8')
|
||||||
cleantagname = utils.normalize_nodes(tagname)
|
cleantagname = utils.normalize_nodes(tagname)
|
||||||
nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
|
nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
|
||||||
|
@ -342,7 +337,7 @@ class VideoNodes(object):
|
||||||
'Favorite tvshows': 30181,
|
'Favorite tvshows': 30181,
|
||||||
'channels': 30173
|
'channels': 30173
|
||||||
}
|
}
|
||||||
label = utils.language(labels[tagname])
|
label = lang(labels[tagname])
|
||||||
embynode = "Emby.nodes.%s" % indexnumber
|
embynode = "Emby.nodes.%s" % indexnumber
|
||||||
window('%s.title' % embynode, value=label)
|
window('%s.title' % embynode, value=label)
|
||||||
window('%s.path' % embynode, value=windowpath)
|
window('%s.path' % embynode, value=windowpath)
|
||||||
|
@ -369,9 +364,7 @@ class VideoNodes(object):
|
||||||
|
|
||||||
def clearProperties(self):
|
def clearProperties(self):
|
||||||
|
|
||||||
window = utils.window
|
log("Clearing nodes properties.", 1)
|
||||||
|
|
||||||
self.logMsg("Clearing nodes properties.", 1)
|
|
||||||
embyprops = window('Emby.nodes.total')
|
embyprops = window('Emby.nodes.total')
|
||||||
propnames = [
|
propnames = [
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,7 @@ import downloadutils
|
||||||
import librarysync
|
import librarysync
|
||||||
import playlist
|
import playlist
|
||||||
import userclient
|
import userclient
|
||||||
import utils
|
from utils import Logging, window, settings, language as lang
|
||||||
|
|
||||||
import logging
|
|
||||||
logging.basicConfig()
|
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -32,6 +29,9 @@ class WebSocket_Client(threading.Thread):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
global log
|
||||||
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
self.monitor = xbmc.Monitor()
|
self.monitor = xbmc.Monitor()
|
||||||
|
|
||||||
|
@ -43,15 +43,10 @@ class WebSocket_Client(threading.Thread):
|
||||||
|
|
||||||
threading.Thread.__init__(self)
|
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):
|
def sendProgressUpdate(self, data):
|
||||||
|
|
||||||
self.logMsg("sendProgressUpdate", 2)
|
log("sendProgressUpdate", 2)
|
||||||
try:
|
try:
|
||||||
messageData = {
|
messageData = {
|
||||||
|
|
||||||
|
@ -60,23 +55,21 @@ class WebSocket_Client(threading.Thread):
|
||||||
}
|
}
|
||||||
messageString = json.dumps(messageData)
|
messageString = json.dumps(messageData)
|
||||||
self.client.send(messageString)
|
self.client.send(messageString)
|
||||||
self.logMsg("Message data: %s" % messageString, 2)
|
log("Message data: %s" % messageString, 2)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logMsg("Exception: %s" % e, 1)
|
log("Exception: %s" % e, 1)
|
||||||
|
|
||||||
def on_message(self, ws, message):
|
def on_message(self, ws, message):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
lang = utils.language
|
|
||||||
|
|
||||||
result = json.loads(message)
|
result = json.loads(message)
|
||||||
messageType = result['MessageType']
|
messageType = result['MessageType']
|
||||||
data = result['Data']
|
data = result['Data']
|
||||||
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
||||||
if messageType not in ('SessionEnded'):
|
if messageType not in ('SessionEnded'):
|
||||||
# Mute certain events
|
# Mute certain events
|
||||||
self.logMsg("Message: %s" % message, 1)
|
log("Message: %s" % message, 1)
|
||||||
|
|
||||||
if messageType == "Play":
|
if messageType == "Play":
|
||||||
# A remote control play command has been sent from the server.
|
# A remote control play command has been sent from the server.
|
||||||
|
@ -84,7 +77,6 @@ class WebSocket_Client(threading.Thread):
|
||||||
command = data['PlayCommand']
|
command = data['PlayCommand']
|
||||||
|
|
||||||
pl = playlist.Playlist()
|
pl = playlist.Playlist()
|
||||||
dialog = xbmcgui.Dialog()
|
|
||||||
|
|
||||||
if command == "PlayNow":
|
if command == "PlayNow":
|
||||||
dialog.notification(
|
dialog.notification(
|
||||||
|
@ -126,10 +118,10 @@ class WebSocket_Client(threading.Thread):
|
||||||
seekto = data['SeekPositionTicks']
|
seekto = data['SeekPositionTicks']
|
||||||
seektime = seekto / 10000000.0
|
seektime = seekto / 10000000.0
|
||||||
action(seektime)
|
action(seektime)
|
||||||
self.logMsg("Seek to %s." % seektime, 1)
|
log("Seek to %s." % seektime, 1)
|
||||||
else:
|
else:
|
||||||
action()
|
action()
|
||||||
self.logMsg("Command: %s completed." % command, 1)
|
log("Command: %s completed." % command, 1)
|
||||||
|
|
||||||
window('emby_command', value="true")
|
window('emby_command', value="true")
|
||||||
|
|
||||||
|
@ -199,7 +191,7 @@ class WebSocket_Client(threading.Thread):
|
||||||
|
|
||||||
header = arguments['Header']
|
header = arguments['Header']
|
||||||
text = arguments['Text']
|
text = arguments['Text']
|
||||||
xbmcgui.Dialog().notification(
|
dialog.notification(
|
||||||
heading=header,
|
heading=header,
|
||||||
message=text,
|
message=text,
|
||||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||||
|
@ -250,8 +242,8 @@ class WebSocket_Client(threading.Thread):
|
||||||
xbmc.executebuiltin(action)
|
xbmc.executebuiltin(action)
|
||||||
|
|
||||||
elif messageType == "ServerRestarting":
|
elif messageType == "ServerRestarting":
|
||||||
if utils.settings('supressRestartMsg') == "true":
|
if settings('supressRestartMsg') == "true":
|
||||||
xbmcgui.Dialog().notification(
|
dialog.notification(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message=lang(33006),
|
message=lang(33006),
|
||||||
icon="special://home/addons/plugin.video.emby/icon.png")
|
icon="special://home/addons/plugin.video.emby/icon.png")
|
||||||
|
@ -262,7 +254,7 @@ class WebSocket_Client(threading.Thread):
|
||||||
self.librarySync.refresh_views = True
|
self.librarySync.refresh_views = True
|
||||||
|
|
||||||
def on_close(self, ws):
|
def on_close(self, ws):
|
||||||
self.logMsg("Closed.", 2)
|
log("Closed.", 2)
|
||||||
|
|
||||||
def on_open(self, ws):
|
def on_open(self, ws):
|
||||||
self.doUtils.postCapabilities(self.deviceId)
|
self.doUtils.postCapabilities(self.deviceId)
|
||||||
|
@ -272,11 +264,10 @@ class WebSocket_Client(threading.Thread):
|
||||||
# Server is offline
|
# Server is offline
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.logMsg("Error: %s" % error, 2)
|
log("Error: %s" % error, 2)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
window = utils.window
|
|
||||||
loglevel = int(window('emby_logLevel'))
|
loglevel = int(window('emby_logLevel'))
|
||||||
# websocket.enableTrace(True)
|
# websocket.enableTrace(True)
|
||||||
|
|
||||||
|
@ -290,7 +281,7 @@ class WebSocket_Client(threading.Thread):
|
||||||
server = server.replace('http', "ws")
|
server = server.replace('http', "ws")
|
||||||
|
|
||||||
websocket_url = "%s?api_key=%s&deviceId=%s" % (server, token, self.deviceId)
|
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,
|
self.client = websocket.WebSocketApp(websocket_url,
|
||||||
on_message=self.on_message,
|
on_message=self.on_message,
|
||||||
|
@ -298,7 +289,7 @@ class WebSocket_Client(threading.Thread):
|
||||||
on_close=self.on_close)
|
on_close=self.on_close)
|
||||||
|
|
||||||
self.client.on_open = self.on_open
|
self.client.on_open = self.on_open
|
||||||
self.logMsg("----===## Starting WebSocketClient ##===----", 0)
|
log("----===## Starting WebSocketClient ##===----", 0)
|
||||||
|
|
||||||
while not self.monitor.abortRequested():
|
while not self.monitor.abortRequested():
|
||||||
|
|
||||||
|
@ -310,10 +301,10 @@ class WebSocket_Client(threading.Thread):
|
||||||
# Abort was requested, exit
|
# Abort was requested, exit
|
||||||
break
|
break
|
||||||
|
|
||||||
self.logMsg("##===---- WebSocketClient Stopped ----===##", 0)
|
log("##===---- WebSocketClient Stopped ----===##", 0)
|
||||||
|
|
||||||
def stopClient(self):
|
def stopClient(self):
|
||||||
|
|
||||||
self.stopWebsocket = True
|
self.stopWebsocket = True
|
||||||
self.client.close()
|
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 = xbmcaddon.Addon(id='plugin.video.emby')
|
||||||
addon_path = _addon.getAddonInfo('path').decode('utf-8')
|
_addon_path = _addon.getAddonInfo('path').decode('utf-8')
|
||||||
base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8')
|
_base_resource = xbmc.translatePath(os.path.join(_addon_path, 'resources', 'lib')).decode('utf-8')
|
||||||
sys.path.append(base_resource)
|
sys.path.append(_base_resource)
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ import initialsetup
|
||||||
import kodimonitor
|
import kodimonitor
|
||||||
import librarysync
|
import librarysync
|
||||||
import player
|
import player
|
||||||
import utils
|
|
||||||
import videonodes
|
import videonodes
|
||||||
import websocket_client as wsc
|
import websocket_client as wsc
|
||||||
|
from utils import Logging, window, settings, language as lang
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ class Service():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
log = self.logMsg
|
global log
|
||||||
window = utils.window
|
log = Logging(self.__class__.__name__).log
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
|
@ -58,15 +58,14 @@ class Service():
|
||||||
self.monitor = xbmc.Monitor()
|
self.monitor = xbmc.Monitor()
|
||||||
|
|
||||||
window('emby_logLevel', value=str(logLevel))
|
window('emby_logLevel', value=str(logLevel))
|
||||||
window('emby_kodiProfile', value=xbmc.translatePath("special://profile"))
|
window('emby_kodiProfile', value=xbmc.translatePath('special://profile'))
|
||||||
window('emby_pluginpath', value=utils.settings('useDirectPaths'))
|
|
||||||
|
|
||||||
# Initial logging
|
# Initial logging
|
||||||
log("======== START %s ========" % self.addonName, 0)
|
log("======== START %s ========" % self.addonName, 0)
|
||||||
log("Platform: %s" % (self.clientInfo.getPlatform()), 0)
|
log("Platform: %s" % (self.clientInfo.getPlatform()), 0)
|
||||||
log("KODI Version: %s" % xbmc.getInfoLabel('System.BuildVersion'), 0)
|
log("KODI Version: %s" % xbmc.getInfoLabel('System.BuildVersion'), 0)
|
||||||
log("%s Version: %s" % (self.addonName, self.clientInfo.getVersion()), 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)
|
log("Log Level: %s" % logLevel, 0)
|
||||||
|
|
||||||
# Reset window props for profile switch
|
# Reset window props for profile switch
|
||||||
|
@ -86,22 +85,13 @@ class Service():
|
||||||
# Set the minimum database version
|
# Set the minimum database version
|
||||||
window('emby_minDBVersion', value="1.1.63")
|
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):
|
def ServiceEntryPoint(self):
|
||||||
|
|
||||||
log = self.logMsg
|
|
||||||
window = utils.window
|
|
||||||
lang = utils.language
|
|
||||||
|
|
||||||
# Important: Threads depending on abortRequest will not trigger
|
# Important: Threads depending on abortRequest will not trigger
|
||||||
# if profile switch happens more than once.
|
# if profile switch happens more than once.
|
||||||
monitor = self.monitor
|
monitor = self.monitor
|
||||||
kodiProfile = xbmc.translatePath("special://profile")
|
kodiProfile = xbmc.translatePath('special://profile')
|
||||||
|
|
||||||
# Server auto-detect
|
# Server auto-detect
|
||||||
initialsetup.InitialSetup().setup()
|
initialsetup.InitialSetup().setup()
|
||||||
|
@ -119,7 +109,7 @@ class Service():
|
||||||
if window('emby_kodiProfile') != kodiProfile:
|
if window('emby_kodiProfile') != kodiProfile:
|
||||||
# Profile change happened, terminate this thread and others
|
# Profile change happened, terminate this thread and others
|
||||||
log("Kodi profile was: %s and changed to: %s. Terminating old Emby thread."
|
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
|
break
|
||||||
|
|
||||||
|
@ -167,7 +157,7 @@ class Service():
|
||||||
else:
|
else:
|
||||||
# Start up events
|
# Start up events
|
||||||
self.warn_auth = True
|
self.warn_auth = True
|
||||||
if utils.settings('connectMsg') == "true" and self.welcome_msg:
|
if settings('connectMsg') == "true" and self.welcome_msg:
|
||||||
# Reset authentication warnings
|
# Reset authentication warnings
|
||||||
self.welcome_msg = False
|
self.welcome_msg = False
|
||||||
# Get additional users
|
# Get additional users
|
||||||
|
@ -291,7 +281,7 @@ class Service():
|
||||||
log("======== STOP %s ========" % self.addonName, 0)
|
log("======== STOP %s ========" % self.addonName, 0)
|
||||||
|
|
||||||
# Delay option
|
# Delay option
|
||||||
delay = int(utils.settings('startupDelay'))
|
delay = int(settings('startupDelay'))
|
||||||
|
|
||||||
xbmc.log("Delaying emby startup by: %s sec..." % delay)
|
xbmc.log("Delaying emby startup by: %s sec..." % delay)
|
||||||
if delay and xbmc.Monitor().waitForAbort(delay):
|
if delay and xbmc.Monitor().waitForAbort(delay):
|
||||||
|
|
Loading…
Reference in a new issue