Update for strings

This commit is contained in:
angelblue05 2016-02-17 02:13:37 -06:00
parent 0ba1ac4104
commit 52a5d35295
10 changed files with 477 additions and 389 deletions

View file

@ -47,7 +47,7 @@ class LibrarySync(threading.Thread):
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
self.doUtils = downloadutils.DownloadUtils()
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.user = userclient.UserClient()
self.emby = embyserver.Read_EmbyServer()
self.vnodes = videonodes.VideoNodes()
@ -72,17 +72,19 @@ class LibrarySync(threading.Thread):
return dialog
def startSync(self):
settings = utils.settings
# Run at start up - optional to use the server plugin
if utils.settings('SyncInstallRunDone') == "true":
if settings('SyncInstallRunDone') == "true":
# Validate views
self.refreshViews()
completed = False
# Verify if server plugin is installed.
if utils.settings('serverSync') == "true":
if settings('serverSync') == "true":
# Try to use fast start up
url = "{server}/emby/Plugins?format=json"
result = self.doUtils.downloadUrl(url)
result = self.doUtils(url)
for plugin in result:
if plugin['Name'] == "Emby.Kodi Sync Queue":
@ -100,30 +102,43 @@ class LibrarySync(threading.Thread):
def fastSync(self):
log = self.logMsg
doUtils = self.doUtils
lastSync = utils.settings('LastIncrementalSync')
if not lastSync:
lastSync = "2010-01-01T00:00:00Z"
lastSyncTime = utils.convertdate(lastSync)
self.logMsg("Last sync run: %s" % lastSyncTime, 1)
log("Last sync run: %s" % lastSyncTime, 1)
# get server RetentionDateTime
url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
result = self.doUtils.downloadUrl(url)
result = doUtils(url)
retention_time = "2010-01-01T00:00:00Z"
if result and result.get('RetentionDateTime'):
retention_time = result['RetentionDateTime']
#Try/except equivalent
'''
try:
retention_time = result['RetentionDateTime']
except (TypeError, KeyError):
retention_time = "2010-01-01T00:00:00Z"
'''
retention_time = utils.convertdate(retention_time)
self.logMsg("RetentionDateTime: %s" % retention_time, 1)
log("RetentionDateTime: %s" % retention_time, 1)
# if last sync before retention time do a full sync
if retention_time > lastSyncTime:
self.logMsg("Fast sync server retention insurficient, fall back to full sync", 1)
log("Fast sync server retention insufficient, fall back to full sync", 1)
return False
url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json"
params = {'LastUpdateDT': lastSync}
result = self.doUtils.downloadUrl(url, parameters=params)
result = doUtils(url, parameters=params)
try:
processlist = {
@ -135,36 +150,38 @@ class LibrarySync(threading.Thread):
}
except (KeyError, TypeError):
self.logMsg("Failed to retrieve latest updates using fast sync.", 1)
log("Failed to retrieve latest updates using fast sync.", 1)
return False
else:
self.logMsg("Fast sync changes: %s" % result, 1)
log("Fast sync changes: %s" % result, 1)
for action in processlist:
self.triage_items(action, processlist[action])
return True
def saveLastSync(self):
log = self.logMsg
# Save last sync time
overlap = 2
url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
result = self.doUtils.downloadUrl(url)
result = self.doUtils(url)
try: # datetime fails when used more than once, TypeError
server_time = result['ServerDateTime']
server_time = utils.convertdate(server_time)
except Exception as e:
# If the server plugin is not installed or an error happened.
self.logMsg("An exception occurred: %s" % e, 1)
log("An exception occurred: %s" % e, 1)
time_now = datetime.utcnow()-timedelta(minutes=overlap)
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
self.logMsg("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
log("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
else:
lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
self.logMsg("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
log("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
finally:
utils.settings('LastIncrementalSync', value=lastSync)
@ -179,32 +196,39 @@ class LibrarySync(threading.Thread):
return False
def dbCommit(self, connection):
log = self.logMsg
window = utils.window
# Central commit, verifies if Kodi database update is running
kodidb_scan = utils.window('emby_kodiScan') == "true"
kodidb_scan = window('emby_kodiScan') == "true"
while kodidb_scan:
self.logMsg("Kodi scan is running. Waiting...", 1)
kodidb_scan = utils.window('emby_kodiScan') == "true"
log("Kodi scan is running. Waiting...", 1)
kodidb_scan = window('emby_kodiScan') == "true"
if self.shouldStop():
self.logMsg("Commit unsuccessful. Sync terminated.", 1)
log("Commit unsuccessful. Sync terminated.", 1)
break
if self.monitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
self.logMsg("Commit unsuccessful.", 1)
log("Commit unsuccessful.", 1)
break
else:
connection.commit()
self.logMsg("Commit successful.", 1)
log("Commit successful.", 1)
def fullSync(self, manualrun=False, repair=False):
log = self.logMsg
window = utils.window
settings = utils.settings
# Only run once when first setting up. Can be run manually.
emby = self.emby
music_enabled = utils.settings('enableMusic') == "true"
utils.window('emby_dbScan', value="true")
window('emby_dbScan', value="true")
# Add sources
utils.sourcesXML()
@ -232,7 +256,7 @@ class LibrarySync(threading.Thread):
message = "Repair sync"
else:
message = "Initial sync"
utils.window('emby_initialScan', value="true")
window('emby_initialScan', value="true")
pDialog = self.progressDialog("%s" % message, forced=True)
starttotal = datetime.now()
@ -253,7 +277,7 @@ class LibrarySync(threading.Thread):
completed = process[itemtype](embycursor, kodicursor, pDialog)
if not completed:
utils.window('emby_dbScan', clear=True)
window('emby_dbScan', clear=True)
if pDialog:
pDialog.close()
@ -264,8 +288,7 @@ class LibrarySync(threading.Thread):
self.dbCommit(kodiconn)
embyconn.commit()
elapsedTime = datetime.now() - startTime
self.logMsg(
"SyncDatabase (finished %s in: %s)"
log("SyncDatabase (finished %s in: %s)"
% (itemtype, str(elapsedTime).split('.')[0]), 1)
else:
# Close the Kodi cursor
@ -281,7 +304,7 @@ class LibrarySync(threading.Thread):
completed = self.music(embycursor, musiccursor, pDialog)
if not completed:
utils.window('emby_dbScan', clear=True)
window('emby_dbScan', clear=True)
if pDialog:
pDialog.close()
@ -292,8 +315,7 @@ class LibrarySync(threading.Thread):
musicconn.commit()
embyconn.commit()
elapsedTime = datetime.now() - startTime
self.logMsg(
"SyncDatabase (finished music in: %s)"
log("SyncDatabase (finished music in: %s)"
% (str(elapsedTime).split('.')[0]), 1)
musiccursor.close()
@ -302,18 +324,18 @@ class LibrarySync(threading.Thread):
embycursor.close()
utils.settings('SyncInstallRunDone', value="true")
utils.settings("dbCreatedWithVersion", self.clientInfo.getVersion())
settings('SyncInstallRunDone', value="true")
settings("dbCreatedWithVersion", self.clientInfo.getVersion())
self.saveLastSync()
xbmc.executebuiltin('UpdateLibrary(video)')
elapsedtotal = datetime.now() - starttotal
utils.window('emby_dbScan', clear=True)
utils.window('emby_initialScan', clear=True)
window('emby_dbScan', clear=True)
window('emby_initialScan', clear=True)
xbmcgui.Dialog().notification(
heading="Emby for Kodi",
message="%s completed in: %s" %
(message, str(elapsedtotal).split('.')[0]),
message="%s %s %s" %
(message, utils.language(33025), str(elapsedtotal).split('.')[0]),
icon="special://home/addons/plugin.video.emby/icon.png",
sound=False)
return True
@ -336,6 +358,8 @@ class LibrarySync(threading.Thread):
embycursor.close()
def maintainViews(self, embycursor, kodicursor):
log = self.logMsg
# Compare the views to emby
emby_db = embydb.Embydb_Functions(embycursor)
kodi_db = kodidb.Kodidb_Functions(kodicursor)
@ -344,14 +368,14 @@ class LibrarySync(threading.Thread):
# Get views
url = "{server}/emby/Users/{UserId}/Views?format=json"
result = doUtils.downloadUrl(url)
result = doUtils(url)
grouped_views = result['Items']
try:
groupedFolders = self.user.userSettings['Configuration']['GroupedFolders']
except TypeError:
url = "{server}/emby/Users/{UserId}?format=json"
result = doUtils.downloadUrl(url)
result = doUtils(url)
groupedFolders = result['Configuration']['GroupedFolders']
# total nodes for window properties
@ -402,7 +426,7 @@ class LibrarySync(threading.Thread):
emby_db.addView(folderid, foldername, viewtype, tagid)
else:
self.logMsg(' '.join((
log(' '.join((
"Found viewid: %s" % folderid,
"viewname: %s" % current_viewname,
@ -411,7 +435,7 @@ class LibrarySync(threading.Thread):
# View was modified, update with latest info
if current_viewname != foldername:
self.logMsg("viewid: %s new viewname: %s" % (folderid, foldername), 1)
log("viewid: %s new viewname: %s" % (folderid, foldername), 1)
tagid = kodi_db.createTag(foldername)
# Update view with new info
@ -466,6 +490,9 @@ class LibrarySync(threading.Thread):
utils.window('Emby.nodes.total', str(totalnodes))
def movies(self, embycursor, kodicursor, pdialog):
log = self.logMsg
lang = utils.language
# Get movies from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
@ -473,7 +500,7 @@ class LibrarySync(threading.Thread):
views = emby_db.getView_byType('movies')
views += emby_db.getView_byType('mixed')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
##### PROCESS MOVIES #####
for view in views:
@ -488,7 +515,7 @@ class LibrarySync(threading.Thread):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Gathering movies from view: %s..." % viewName)
message="%s %s..." % (lang(33017), viewName))
# Initial or repair sync
all_embymovies = emby.getMovies(viewId, dialog=pdialog)
@ -511,12 +538,12 @@ class LibrarySync(threading.Thread):
count += 1
movies.add_update(embymovie, viewName, viewId)
else:
self.logMsg("Movies finished.", 2)
log("Movies finished.", 2)
##### PROCESS BOXSETS #####
if pdialog:
pdialog.update(heading="Emby for Kodi", message="Gathering boxsets from server...")
pdialog.update(heading="Emby for Kodi", message=lang(33018))
boxsets = emby.getBoxset(dialog=pdialog)
total = boxsets['TotalRecordCount']
@ -538,18 +565,20 @@ class LibrarySync(threading.Thread):
count += 1
movies.add_updateBoxset(boxset)
else:
self.logMsg("Boxsets finished.", 2)
log("Boxsets finished.", 2)
return True
def musicvideos(self, embycursor, kodicursor, pdialog):
log = self.logMsg
# Get musicvideos from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
views = emby_db.getView_byType('musicvideos')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
for view in views:
@ -563,7 +592,7 @@ class LibrarySync(threading.Thread):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Gathering musicvideos from view: %s..." % viewName)
message="%s %s..." % (utils.language(33019), viewName))
# Initial or repair sync
all_embymvideos = emby.getMusicVideos(viewId, dialog=pdialog)
@ -586,11 +615,13 @@ class LibrarySync(threading.Thread):
count += 1
mvideos.add_update(embymvideo, viewName, viewId)
else:
self.logMsg("MusicVideos finished.", 2)
log("MusicVideos finished.", 2)
return True
def tvshows(self, embycursor, kodicursor, pdialog):
log = self.logMsg
# Get shows from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
@ -598,7 +629,7 @@ class LibrarySync(threading.Thread):
views = emby_db.getView_byType('tvshows')
views += emby_db.getView_byType('mixed')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
for view in views:
@ -612,7 +643,7 @@ class LibrarySync(threading.Thread):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Gathering tvshows from view: %s..." % viewName)
message="%s %s..." % (utils.language(33020), viewName))
all_embytvshows = emby.getShows(viewId, dialog=pdialog)
total = all_embytvshows['TotalRecordCount']
@ -648,7 +679,7 @@ class LibrarySync(threading.Thread):
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
tvshows.add_updateEpisode(episode)
else:
self.logMsg("TVShows finished.", 2)
log("TVShows finished.", 2)
return True
@ -665,19 +696,19 @@ class LibrarySync(threading.Thread):
'songs': [emby.getSongs, music.add_updateSong]
}
types = ['artists', 'albums', 'songs']
for type in types:
for itemtype in types:
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Gathering %s..." % type)
message="%s %s..." % (utils.language(33021), itemtype))
all_embyitems = process[type][0](dialog=pdialog)
all_embyitems = process[itemtype][0](dialog=pdialog)
total = all_embyitems['TotalRecordCount']
embyitems = all_embyitems['Items']
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (type, total))
pdialog.update(heading="Processing %s / %s items" % (itemtype, total))
count = 0
for embyitem in embyitems:
@ -691,9 +722,9 @@ class LibrarySync(threading.Thread):
pdialog.update(percentage, message=title)
count += 1
process[type][1](embyitem)
process[itemtype][1](embyitem)
else:
self.logMsg("%s finished." % type, 2)
self.logMsg("%s finished." % itemtype, 2)
return True
@ -719,6 +750,8 @@ class LibrarySync(threading.Thread):
def incrementalSync(self):
log = self.logMsg
embyconn = utils.kodiSQL('emby')
embycursor = embyconn.cursor()
kodiconn = utils.kodiSQL('video')
@ -788,7 +821,7 @@ class LibrarySync(threading.Thread):
if update_embydb:
update_embydb = False
self.logMsg("Updating emby database.", 1)
log("Updating emby database.", 1)
embyconn.commit()
self.saveLastSync()
@ -797,7 +830,7 @@ class LibrarySync(threading.Thread):
self.forceLibraryUpdate = False
self.dbCommit(kodiconn)
self.logMsg("Updating video library.", 1)
log("Updating video library.", 1)
utils.window('emby_kodiScan', value="true")
xbmc.executebuiltin('UpdateLibrary(video)')
@ -839,10 +872,16 @@ class LibrarySync(threading.Thread):
def run_internal(self):
log = self.logMsg
lang = utils.language
window = utils.window
settings = utils.settings
dialog = xbmcgui.Dialog()
startupComplete = False
monitor = self.monitor
self.logMsg("---===### Starting LibrarySync ###===---", 0)
log("---===### Starting LibrarySync ###===---", 0)
while not monitor.abortRequested():
@ -853,36 +892,24 @@ class LibrarySync(threading.Thread):
# Abort was requested while waiting. We should exit
break
if (utils.window('emby_dbCheck') != "true" and
utils.settings('SyncInstallRunDone') == "true"):
if (window('emby_dbCheck') != "true" and settings('SyncInstallRunDone') == "true"):
# Verify the validity of the database
currentVersion = utils.settings('dbCreatedWithVersion')
minVersion = utils.window('emby_minDBVersion')
currentVersion = settings('dbCreatedWithVersion')
minVersion = window('emby_minDBVersion')
uptoDate = self.compareDBVersion(currentVersion, minVersion)
if not uptoDate:
self.logMsg(
"Db version out of date: %s minimum version required: %s"
log("Database version out of date: %s minimum version required: %s"
% (currentVersion, minVersion), 0)
resp = xbmcgui.Dialog().yesno(
heading="Db Version",
line1=(
"Detected the database needs to be "
"recreated for this version of Emby for Kodi. "
"Proceed?"))
resp = dialog.yesno("Emby for Kodi", lang(33022))
if not resp:
self.logMsg("Db version out of date! USER IGNORED!", 0)
xbmcgui.Dialog().ok(
heading="Emby for Kodi",
line1=(
"Emby for Kodi may not work correctly "
"until the database is reset."))
log("Database version is out of date! USER IGNORED!", 0)
dialog.ok("Emby for Kodi", lang(33023))
else:
utils.reset()
utils.window('emby_dbCheck', value="true")
window('emby_dbCheck', value="true")
if not startupComplete:
@ -890,58 +917,52 @@ class LibrarySync(threading.Thread):
videoDb = utils.getKodiVideoDBPath()
if not xbmcvfs.exists(videoDb):
# Database does not exists
self.logMsg(
log(
"The current Kodi version is incompatible "
"with the Emby for Kodi add-on. Please visit "
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
"to know which Kodi versions are supported.", 0)
xbmcgui.Dialog().ok(
heading="Emby Warning",
line1=(
"Cancelling the database syncing process. "
"Current Kodi versoin: %s is unsupported. "
"Please verify your logs for more info."
% xbmc.getInfoLabel('System.BuildVersion')))
dialog.ok(
heading="Emby for Kodi",
line1=lang(33024))
break
# Run start up sync
self.logMsg("Db version: %s" % utils.settings('dbCreatedWithVersion'), 0)
self.logMsg("SyncDatabase (started)", 1)
log("Database version: %s" % settings('dbCreatedWithVersion'), 0)
log("SyncDatabase (started)", 1)
startTime = datetime.now()
librarySync = self.startSync()
elapsedTime = datetime.now() - startTime
self.logMsg(
"SyncDatabase (finished in: %s) %s"
log("SyncDatabase (finished in: %s) %s"
% (str(elapsedTime).split('.')[0], librarySync), 1)
# Only try the initial sync once per kodi session regardless
# This will prevent an infinite loop in case something goes wrong.
startupComplete = True
# Process updates
if utils.window('emby_dbScan') != "true":
if window('emby_dbScan') != "true":
self.incrementalSync()
if (utils.window('emby_onWake') == "true" and
utils.window('emby_online') == "true"):
if window('emby_onWake') == "true" and window('emby_online') == "true":
# Kodi is waking up
# Set in kodimonitor.py
utils.window('emby_onWake', clear=True)
if utils.window('emby_syncRunning') != "true":
self.logMsg("SyncDatabase onWake (started)", 0)
window('emby_onWake', clear=True)
if window('emby_syncRunning') != "true":
log("SyncDatabase onWake (started)", 0)
librarySync = self.startSync()
self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0)
log("SyncDatabase onWake (finished) %s" % librarySync, 0)
if self.stop_thread:
# Set in service.py
self.logMsg("Service terminated thread.", 2)
log("Service terminated thread.", 2)
break
if monitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
break
self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
log("###===--- LibrarySync Stopped ---===###", 0)
def stopThread(self):
self.stop_thread = True
@ -966,6 +987,9 @@ class ManualSync(LibrarySync):
def movies(self, embycursor, kodicursor, pdialog):
log = self.logMsg
lang = utils.language
# Get movies from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
@ -973,7 +997,7 @@ class ManualSync(LibrarySync):
views = emby_db.getView_byType('movies')
views += emby_db.getView_byType('mixed')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
# Pull the list of movies and boxsets in Kodi
try:
@ -1003,7 +1027,7 @@ class ManualSync(LibrarySync):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Comparing movies from view: %s..." % viewName)
message="%s %s..." % (lang(33026), viewName))
all_embymovies = emby.getMovies(viewId, basic=True, dialog=pdialog)
for embymovie in all_embymovies['Items']:
@ -1020,7 +1044,7 @@ class ManualSync(LibrarySync):
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
self.logMsg("Movies to update for %s: %s" % (viewName, updatelist), 1)
log("Movies to update for %s: %s" % (viewName, updatelist), 1)
embymovies = emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
@ -1047,9 +1071,7 @@ class ManualSync(LibrarySync):
embyboxsets = []
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Comparing boxsets...")
pdialog.update("Emby for Kodi", lang(33027))
for boxset in boxsets['Items']:
@ -1066,7 +1088,7 @@ class ManualSync(LibrarySync):
updatelist.append(itemid)
embyboxsets.append(boxset)
self.logMsg("Boxsets to update: %s" % updatelist, 1)
log("Boxsets to update: %s" % updatelist, 1)
total = len(updatelist)
if pdialog:
@ -1091,24 +1113,26 @@ class ManualSync(LibrarySync):
if kodimovie not in all_embymoviesIds:
movies.remove(kodimovie)
else:
self.logMsg("Movies compare finished.", 1)
log("Movies compare finished.", 1)
for boxset in all_kodisets:
if boxset not in all_embyboxsetsIds:
movies.remove(boxset)
else:
self.logMsg("Boxsets compare finished.", 1)
log("Boxsets compare finished.", 1)
return True
def musicvideos(self, embycursor, kodicursor, pdialog):
log = self.logMsg
# Get musicvideos from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
views = emby_db.getView_byType('musicvideos')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
# Pull the list of musicvideos in Kodi
try:
@ -1131,7 +1155,7 @@ class ManualSync(LibrarySync):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Comparing musicvideos from view: %s..." % viewName)
message="%s %s..." % (utils.language(33028), viewName))
all_embymvideos = emby.getMusicVideos(viewId, basic=True, dialog=pdialog)
for embymvideo in all_embymvideos['Items']:
@ -1148,7 +1172,7 @@ class ManualSync(LibrarySync):
# Only update if musicvideo is not in Kodi or checksum is different
updatelist.append(itemid)
self.logMsg("MusicVideos to update for %s: %s" % (viewName, updatelist), 1)
log("MusicVideos to update for %s: %s" % (viewName, updatelist), 1)
embymvideos = emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
@ -1176,11 +1200,14 @@ class ManualSync(LibrarySync):
if kodimvideo not in all_embymvideosIds:
mvideos.remove(kodimvideo)
else:
self.logMsg("MusicVideos compare finished.", 1)
log("MusicVideos compare finished.", 1)
return True
def tvshows(self, embycursor, kodicursor, pdialog):
log = self.logMsg
lang = utils.language
# Get shows from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
@ -1188,7 +1215,7 @@ class ManualSync(LibrarySync):
views = emby_db.getView_byType('tvshows')
views += emby_db.getView_byType('mixed')
self.logMsg("Media folders: %s" % views, 1)
log("Media folders: %s" % views, 1)
# Pull the list of tvshows and episodes in Kodi
try:
@ -1218,7 +1245,7 @@ class ManualSync(LibrarySync):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Comparing tvshows from view: %s..." % viewName)
message="%s %s..." % (lang(33029), viewName))
all_embytvshows = emby.getShows(viewId, basic=True, dialog=pdialog)
for embytvshow in all_embytvshows['Items']:
@ -1235,7 +1262,7 @@ class ManualSync(LibrarySync):
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
self.logMsg("TVShows to update for %s: %s" % (viewName, updatelist), 1)
log("TVShows to update for %s: %s" % (viewName, updatelist), 1)
embytvshows = emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
@ -1263,7 +1290,7 @@ class ManualSync(LibrarySync):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Comparing episodes from view: %s..." % viewName)
message="%s %s..." % (lang(33030), viewName))
all_embyepisodes = emby.getEpisodes(viewId, basic=True, dialog=pdialog)
for embyepisode in all_embyepisodes['Items']:
@ -1279,7 +1306,7 @@ class ManualSync(LibrarySync):
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
self.logMsg("Episodes to update for %s: %s" % (viewName, updatelist), 1)
log("Episodes to update for %s: %s" % (viewName, updatelist), 1)
embyepisodes = emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
@ -1305,17 +1332,19 @@ class ManualSync(LibrarySync):
if koditvshow not in all_embytvshowsIds:
tvshows.remove(koditvshow)
else:
self.logMsg("TVShows compare finished.", 1)
log("TVShows compare finished.", 1)
for kodiepisode in all_kodiepisodes:
if kodiepisode not in all_embyepisodesIds:
tvshows.remove(kodiepisode)
else:
self.logMsg("Episodes compare finished.", 1)
log("Episodes compare finished.", 1)
return True
def music(self, embycursor, kodicursor, pdialog):
log = self.logMsg
# Get music from emby
emby = self.emby
emby_db = embydb.Embydb_Functions(embycursor)
@ -1354,7 +1383,7 @@ class ManualSync(LibrarySync):
if pdialog:
pdialog.update(
heading="Emby for Kodi",
message="Comparing %s..." % type)
message="%s %s..." % (utils.language(33031), type))
if type != "artists":
all_embyitems = process[type][0](basic=True, dialog=pdialog)
@ -1383,7 +1412,7 @@ class ManualSync(LibrarySync):
# Only update if songs is not in Kodi or checksum is different
updatelist.append(itemid)
self.logMsg("%s to update: %s" % (type, updatelist), 1)
log("%s to update: %s" % (type, updatelist), 1)
embyitems = emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
@ -1411,18 +1440,18 @@ class ManualSync(LibrarySync):
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
music.remove(kodiartist)
else:
self.logMsg("Artist compare finished.", 1)
log("Artist compare finished.", 1)
for kodialbum in all_kodialbums:
if kodialbum not in all_embyalbumsIds:
music.remove(kodialbum)
else:
self.logMsg("Albums compare finished.", 1)
log("Albums compare finished.", 1)
for kodisong in all_kodisongs:
if kodisong not in all_embysongsIds:
music.remove(kodisong)
else:
self.logMsg("Songs compare finished.", 1)
log("Songs compare finished.", 1)
return True

View file

@ -31,7 +31,7 @@ class PlaybackUtils():
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
self.doUtils = downloadutils.DownloadUtils()
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.userid = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userid)
@ -48,7 +48,9 @@ class PlaybackUtils():
def play(self, itemid, dbid=None):
self.logMsg("Play called.", 1)
log = self.logMsg
window = utils.window
settings = utils.settings
doUtils = self.doUtils
item = self.item
@ -56,6 +58,7 @@ class PlaybackUtils():
listitem = xbmcgui.ListItem()
playutils = putils.PlayUtils(item)
log("Play called.", 1)
playurl = playutils.getPlayUrl()
if not playurl:
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
@ -74,13 +77,13 @@ class PlaybackUtils():
sizePlaylist = playlist.size()
currentPosition = startPos
propertiesPlayback = utils.window('emby_playbackProps') == "true"
propertiesPlayback = window('emby_playbackProps') == "true"
introsPlaylist = False
dummyPlaylist = False
self.logMsg("Playlist start position: %s" % startPos, 1)
self.logMsg("Playlist plugin position: %s" % currentPosition, 1)
self.logMsg("Playlist size: %s" % sizePlaylist, 1)
log("Playlist start position: %s" % startPos, 2)
log("Playlist plugin position: %s" % currentPosition, 2)
log("Playlist size: %s" % sizePlaylist, 2)
############### RESUME POINT ################
@ -91,13 +94,13 @@ class PlaybackUtils():
# Otherwise we get a loop.
if not propertiesPlayback:
utils.window('emby_playbackProps', value="true")
self.logMsg("Setting up properties in playlist.", 1)
window('emby_playbackProps', value="true")
log("Setting up properties in playlist.", 1)
if (not homeScreen and not seektime and
utils.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
playlist.add(playurl, listitem, index=startPos)
# Remove the original item from playlist
@ -108,27 +111,27 @@ class PlaybackUtils():
############### -- CHECK FOR INTROS ################
if utils.settings('enableCinema') == "true" and not seektime:
if settings('enableCinema') == "true" and not seektime:
# if we have any play them when the movie/show is not being resumed
url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid
intros = doUtils.downloadUrl(url)
intros = doUtils(url)
if intros['TotalRecordCount'] != 0:
getTrailers = True
if utils.settings('askCinema') == "true":
resp = xbmcgui.Dialog().yesno("Emby Cinema Mode", "Play trailers?")
if settings('askCinema') == "true":
resp = xbmcgui.Dialog().yesno("Emby for Kodi", utils.language(33016))
if not resp:
# User selected to not play trailers
getTrailers = False
self.logMsg("Skip trailers.", 1)
log("Skip trailers.", 1)
if getTrailers:
for intro in intros['Items']:
# The server randomly returns intros, process them.
introListItem = xbmcgui.ListItem()
introPlayurl = putils.PlayUtils(intro).getPlayUrl()
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
log("Adding Intro: %s" % introPlayurl, 1)
# Set listitem and properties for intros
pbutils = PlaybackUtils(intro)
@ -144,7 +147,7 @@ class PlaybackUtils():
if homeScreen and not seektime and not sizePlaylist:
# Extend our current playlist with the actual item to play
# only if there's no playlist first
self.logMsg("Adding main item to playlist.", 1)
log("Adding main item to playlist.", 1)
self.pl.addtoPlaylist(dbid, item['Type'].lower())
# Ensure that additional parts are played after the main item
@ -156,12 +159,12 @@ class PlaybackUtils():
# Only add to the playlist after intros have played
partcount = item['PartCount']
url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
parts = doUtils.downloadUrl(url)
parts = doUtils(url)
for part in parts['Items']:
additionalListItem = xbmcgui.ListItem()
additionalPlayurl = putils.PlayUtils(part).getPlayUrl()
self.logMsg("Adding additional part: %s" % partcount, 1)
log("Adding additional part: %s" % partcount, 1)
# Set listitem and properties for each additional parts
pbutils = PlaybackUtils(part)
@ -175,58 +178,60 @@ class PlaybackUtils():
if dummyPlaylist:
# Added a dummy file to the playlist,
# because the first item is going to fail automatically.
self.logMsg("Processed as a playlist. First item is skipped.", 1)
log("Processed as a playlist. First item is skipped.", 1)
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
# We just skipped adding properties. Reset flag for next time.
elif propertiesPlayback:
self.logMsg("Resetting properties playback flag.", 2)
utils.window('emby_playbackProps', clear=True)
log("Resetting properties playback flag.", 2)
window('emby_playbackProps', clear=True)
#self.pl.verifyPlaylist()
########## SETUP MAIN ITEM ##########
# For transcoding only, ask for audio/subs pref
if utils.window('emby_%s.playmethod' % playurl) == "Transcode":
if window('emby_%s.playmethod' % playurl) == "Transcode":
playurl = playutils.audioSubsPref(playurl, listitem)
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
window('emby_%s.playmethod' % playurl, value="Transcode")
listitem.setPath(playurl)
self.setProperties(playurl, listitem)
############### PLAYBACK ################
if homeScreen and seektime and utils.window('emby_customPlaylist') != "true":
self.logMsg("Play as a widget item.", 1)
if homeScreen and seektime and window('emby_customPlaylist') != "true":
log("Play as a widget item.", 1)
self.setListItem(listitem)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
elif ((introsPlaylist and utils.window('emby_customPlaylist') == "true") or
elif ((introsPlaylist and window('emby_customPlaylist') == "true") or
(homeScreen and not sizePlaylist)):
# Playlist was created just now, play it.
self.logMsg("Play playlist.", 1)
log("Play playlist.", 1)
xbmc.Player().play(playlist, startpos=startPos)
else:
self.logMsg("Play as a regular item.", 1)
log("Play as a regular item.", 1)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
def setProperties(self, playurl, listitem):
window = utils.window
# Set all properties necessary for plugin path playback
item = self.item
itemid = item['Id']
itemtype = item['Type']
embyitem = "emby_%s" % playurl
utils.window('%s.runtime' % embyitem, value=str(item.get('RunTimeTicks')))
utils.window('%s.type' % embyitem, value=itemtype)
utils.window('%s.itemid' % embyitem, value=itemid)
window('%s.runtime' % embyitem, value=str(item.get('RunTimeTicks')))
window('%s.type' % embyitem, value=itemtype)
window('%s.itemid' % embyitem, value=itemid)
if itemtype == "Episode":
utils.window('%s.refreshid' % embyitem, value=item.get('SeriesId'))
window('%s.refreshid' % embyitem, value=item.get('SeriesId'))
else:
utils.window('%s.refreshid' % embyitem, value=itemid)
window('%s.refreshid' % embyitem, value=itemid)
# Append external subtitles to stream
playmethod = utils.window('%s.playmethod' % embyitem)
@ -316,7 +321,7 @@ class PlaybackUtils():
def setListItem(self, listItem):
item = self.item
type = item['Type']
itemtype = item['Type']
API = self.API
people = API.getPeople()
studios = API.getStudios()
@ -336,7 +341,7 @@ class PlaybackUtils():
'votes': item.get('VoteCount')
}
if "Episode" in type:
if "Episode" in itemtype:
# Only for tv shows
thumbId = item.get('SeriesId')
season = item.get('ParentIndexNumber', -1)

View file

@ -32,7 +32,7 @@ class Player(xbmc.Player):
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
self.doUtils = downloadutils.DownloadUtils()
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.ws = wsc.WebSocket_Client()
self.xbmcplayer = xbmc.Player()
@ -48,6 +48,9 @@ class Player(xbmc.Player):
return self.playStats
def onPlayBackStarted(self):
log = self.logMsg
window = utils.window
# Will be called when xbmc starts playing a file
xbmcplayer = self.xbmcplayer
self.stopAll()
@ -66,7 +69,7 @@ class Player(xbmc.Player):
except: pass
if count == 5: # try 5 times
self.logMsg("Cancelling playback report...", 1)
log("Cancelling playback report...", 1)
break
else: count += 1
@ -76,36 +79,34 @@ class Player(xbmc.Player):
self.currentFile = currentFile
# We may need to wait for info to be set in kodi monitor
itemId = utils.window("emby_%s.itemid" % currentFile)
itemId = window("emby_%s.itemid" % currentFile)
tryCount = 0
while not itemId:
xbmc.sleep(200)
itemId = utils.window("emby_%s.itemid" % currentFile)
itemId = window("emby_%s.itemid" % currentFile)
if tryCount == 20: # try 20 times or about 10 seconds
self.logMsg("Could not find itemId, cancelling playback report...", 1)
log("Could not find itemId, cancelling playback report...", 1)
break
else: tryCount += 1
else:
self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
log("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
# Only proceed if an itemId was found.
embyitem = "emby_%s" % currentFile
runtime = utils.window("%s.runtime" % embyitem)
refresh_id = utils.window("%s.refreshid" % embyitem)
playMethod = utils.window("%s.playmethod" % embyitem)
itemType = utils.window("%s.type" % embyitem)
utils.window('emby_skipWatched%s' % itemId, value="true")
runtime = window("%s.runtime" % embyitem)
refresh_id = window("%s.refreshid" % embyitem)
playMethod = window("%s.playmethod" % embyitem)
itemType = window("%s.type" % embyitem)
window('emby_skipWatched%s' % itemId, value="true")
if (utils.window('emby_customPlaylist') == "true" and
utils.window('emby_customPlaylist.seektime')):
customseek = window('emby_customPlaylist.seektime')
if window('emby_customPlaylist') == "true" and customseek:
# Start at, when using custom playlist (play to Kodi from webclient)
seektime = utils.window('emby_customPlaylist.seektime')
self.logMsg("Seeking to: %s" % seektime, 1)
xbmcplayer.seekTime(int(seektime)/10000000.0)
utils.window('emby_customPlaylist.seektime', clear=True)
log("Seeking to: %s" % customseek, 1)
xbmcplayer.seekTime(int(customseek)/10000000.0)
window('emby_customPlaylist.seektime', clear=True)
seekTime = xbmcplayer.getTime()
@ -144,9 +145,8 @@ class Player(xbmc.Player):
# Get the current audio track and subtitles
if playMethod == "Transcode":
# property set in PlayUtils.py
postdata['AudioStreamIndex'] = utils.window("%sAudioStreamIndex" % currentFile)
postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex"
% currentFile)
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
else:
# Get the current kodi audio and subtitles and convert to Emby equivalent
tracks_query = {
@ -187,11 +187,11 @@ class Player(xbmc.Player):
# Number of audiotracks to help get Emby Index
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
mapping = utils.window("%s.indexMapping" % embyitem)
mapping = window("%s.indexMapping" % embyitem)
if mapping: # Set in playbackutils.py
self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
log("Mapping for external subtitles index: %s" % mapping, 2)
externalIndex = json.loads(mapping)
if externalIndex.get(str(indexSubs)):
@ -209,15 +209,15 @@ class Player(xbmc.Player):
# Post playback to server
self.logMsg("Sending POST play started: %s." % postdata, 2)
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
log("Sending POST play started: %s." % postdata, 2)
self.doUtils(url, postBody=postdata, type="POST")
# Ensure we do have a runtime
try:
runtime = int(runtime)
except ValueError:
runtime = xbmcplayer.getTotalTime()
self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
log("Runtime is missing, Kodi runtime: %s" % runtime, 1)
# Save data map for updates and position calls
data = {
@ -234,7 +234,7 @@ class Player(xbmc.Player):
}
self.played_info[currentFile] = data
self.logMsg("ADDING_FILE: %s" % self.played_info, 1)
log("ADDING_FILE: %s" % self.played_info, 1)
# log some playback stats
'''if(itemType != None):
@ -253,7 +253,9 @@ class Player(xbmc.Player):
def reportPlayback(self):
self.logMsg("reportPlayback Called", 2)
log = self.logMsg
log("reportPlayback Called", 2)
xbmcplayer = self.xbmcplayer
# Get current file
@ -352,7 +354,7 @@ class Player(xbmc.Player):
if mapping: # Set in PlaybackUtils.py
self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
log("Mapping for external subtitles index: %s" % mapping, 2)
externalIndex = json.loads(mapping)
if externalIndex.get(str(indexSubs)):
@ -372,7 +374,7 @@ class Player(xbmc.Player):
# Report progress via websocketclient
postdata = json.dumps(postdata)
self.logMsg("Report: %s" % postdata, 2)
log("Report: %s" % postdata, 2)
self.ws.sendProgressUpdate(postdata)
def onPlayBackPaused( self ):
@ -407,12 +409,15 @@ class Player(xbmc.Player):
self.reportPlayback()
def onPlayBackStopped( self ):
log = self.logMsg
window = utils.window
# Will be called when user stops xbmc playing a file
self.logMsg("ONPLAYBACK_STOPPED", 2)
utils.window('emby_customPlaylist', clear=True)
utils.window('emby_customPlaylist.seektime', clear=True)
utils.window('emby_playbackProps', clear=True)
self.logMsg("Clear playlist properties.", 1)
logMsg("ONPLAYBACK_STOPPED", 2)
window('emby_customPlaylist', clear=True)
window('emby_customPlaylist.seektime', clear=True)
window('emby_playbackProps', clear=True)
logMsg("Clear playlist properties.", 1)
self.stopAll()
def onPlayBackEnded( self ):
@ -423,20 +428,24 @@ class Player(xbmc.Player):
def stopAll(self):
log = self.logMsg
lang = utils.language
settings = utils.settings
doUtils = self.doUtils
if not self.played_info:
return
self.logMsg("Played_information: %s" % self.played_info, 1)
log("Played_information: %s" % self.played_info, 1)
# Process each items
for item in self.played_info:
data = self.played_info.get(item)
if data:
self.logMsg("Item path: %s" % item, 2)
self.logMsg("Item data: %s" % data, 2)
log("Item path: %s" % item, 2)
log("Item data: %s" % data, 2)
runtime = data['runtime']
currentPosition = data['currentPosition']
@ -453,9 +462,8 @@ class Player(xbmc.Player):
# Runtime is 0.
percentComplete = 0
markPlayedAt = float(utils.settings('markPlayed')) / 100
self.logMsg(
"Percent complete: %s Mark played at: %s"
markPlayedAt = float(settings('markPlayed')) / 100
log("Percent complete: %s Mark played at: %s"
% (percentComplete, markPlayedAt), 1)
# Prevent manually mark as watched in Kodi monitor
@ -464,34 +472,32 @@ class Player(xbmc.Player):
self.stopPlayback(data)
# Stop transcoding
if playMethod == "Transcode":
self.logMsg("Transcoding for %s terminated." % itemid, 1)
log("Transcoding for %s terminated." % itemid, 1)
deviceId = self.clientInfo.getDeviceId()
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
doUtils.downloadUrl(url, type="DELETE")
doUtils(url, type="DELETE")
# Send the delete action to the server.
offerDelete = False
if type == "Episode" and utils.settings('deleteTV') == "true":
if type == "Episode" and settings('deleteTV') == "true":
offerDelete = True
elif type == "Movie" and utils.settings('deleteMovies') == "true":
elif type == "Movie" and settings('deleteMovies') == "true":
offerDelete = True
if utils.settings('offerDelete') != "true":
if settings('offerDelete') != "true":
# Delete could be disabled, even if the subsetting is enabled.
offerDelete = False
if percentComplete >= markPlayedAt and offerDelete:
resp = xbmcgui.Dialog().yesno(
heading="Confirm delete",
line1="Delete file on Emby Server?")
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015))
if not resp:
self.logMsg("User skipped deletion.", 1)
log("User skipped deletion.", 1)
continue
url = "{server}/emby/Items/%s?format=json" % itemid
self.logMsg("Deleting request: %s" % itemid)
doUtils.downloadUrl(url, type="DELETE")
log("Deleting request: %s" % itemid, 1)
doUtils(url, type="DELETE")
self.played_info.clear()
@ -510,4 +516,4 @@ class Player(xbmc.Player):
'MediaSourceId': itemId,
'PositionTicks': positionTicks
}
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
self.doUtils(url, postBody=postdata, type="POST")

View file

@ -39,6 +39,9 @@ class Playlist():
def playAll(self, itemids, startat):
log = self.logMsg
window = utils.window
embyconn = utils.kodiSQL('emby')
embycursor = embyconn.cursor()
emby_db = embydb.Embydb_Functions(embycursor)
@ -47,15 +50,15 @@ class Playlist():
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
self.logMsg("---*** PLAY ALL ***---", 1)
self.logMsg("Items: %s and start at: %s" % (itemids, startat))
log("---*** PLAY ALL ***---", 1)
log("Items: %s and start at: %s" % (itemids, startat), 1)
started = False
utils.window('emby_customplaylist', value="true")
window('emby_customplaylist', value="true")
if startat != 0:
# Seek to the starting position
utils.window('emby_customplaylist.seektime', str(startat))
window('emby_customplaylist.seektime', str(startat))
for itemid in itemids:
embydb_item = emby_db.getItem_byId(itemid)
@ -64,14 +67,14 @@ class Playlist():
mediatype = embydb_item[4]
except TypeError:
# Item is not found in our database, add item manually
self.logMsg("Item was not found in the database, manually adding item.", 1)
log("Item was not found in the database, manually adding item.", 1)
item = self.emby.getItem(itemid)
self.addtoPlaylist_xbmc(playlist, item)
else:
# Add to playlist
self.addtoPlaylist(dbid, mediatype)
self.logMsg("Adding %s to playlist." % itemid, 1)
log("Adding %s to playlist." % itemid, 1)
if not started:
started = True
@ -82,12 +85,14 @@ class Playlist():
def modifyPlaylist(self, itemids):
log = self.logMsg
embyconn = utils.kodiSQL('emby')
embycursor = embyconn.cursor()
emby_db = embydb.Embydb_Functions(embycursor)
self.logMsg("---*** ADD TO PLAYLIST ***---", 1)
self.logMsg("Items: %s" % itemids, 1)
log("---*** ADD TO PLAYLIST ***---", 1)
log("Items: %s" % itemids, 1)
player = xbmc.Player()
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
@ -105,7 +110,7 @@ class Playlist():
# Add to playlist
self.addtoPlaylist(dbid, mediatype)
self.logMsg("Adding %s to playlist." % itemid, 1)
log("Adding %s to playlist." % itemid, 1)
self.verifyPlaylist()
embycursor.close()

View file

@ -33,43 +33,46 @@ class PlayUtils():
def getPlayUrl(self):
log = self.logMsg
window = utils.window
item = self.item
playurl = None
if (item.get('Type') in ("Recording", "TvChannel") and
item.get('MediaSources') and item['MediaSources'][0]['Protocol'] == "Http"):
# Play LiveTV or recordings
self.logMsg("File protocol is http (livetv).", 1)
log("File protocol is http (livetv).", 1)
playurl = "%s/emby/Videos/%s/live.m3u8?static=true" % (self.server, item['Id'])
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
window('emby_%s.playmethod' % playurl, value="Transcode")
elif item.get('MediaSources') and item['MediaSources'][0]['Protocol'] == "Http":
# Only play as http, used for channels, or online hosting of content
self.logMsg("File protocol is http.", 1)
log("File protocol is http.", 1)
playurl = self.httpPlay()
utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
window('emby_%s.playmethod' % playurl, value="DirectStream")
elif self.isDirectPlay():
self.logMsg("File is direct playing.", 1)
log("File is direct playing.", 1)
playurl = self.directPlay()
playurl = playurl.encode('utf-8')
# Set playmethod property
utils.window('emby_%s.playmethod' % playurl, value="DirectPlay")
window('emby_%s.playmethod' % playurl, value="DirectPlay")
elif self.isDirectStream():
self.logMsg("File is direct streaming.", 1)
log("File is direct streaming.", 1)
playurl = self.directStream()
# Set playmethod property
utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
window('emby_%s.playmethod' % playurl, value="DirectStream")
elif self.isTranscoding():
self.logMsg("File is transcoding.", 1)
log("File is transcoding.", 1)
playurl = self.transcoding()
# Set playmethod property
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
window('emby_%s.playmethod' % playurl, value="Transcode")
return playurl
@ -90,16 +93,21 @@ class PlayUtils():
def isDirectPlay(self):
log = self.logMsg
lang = utils.language
settings = utils.settings
dialog = xbmcgui.Dialog()
item = self.item
# Requirement: Filesystem, Accessible path
if utils.settings('playFromStream') == "true":
if settings('playFromStream') == "true":
# User forcing to play via HTTP
self.logMsg("Can't direct play, play from HTTP enabled.", 1)
log("Can't direct play, play from HTTP enabled.", 1)
return False
videotrack = item['MediaSources'][0]['Name']
transcodeH265 = utils.settings('transcodeH265')
transcodeH265 = settings('transcodeH265')
if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack):
# Avoid H265/HEVC depending on the resolution
@ -110,7 +118,7 @@ class PlayUtils():
'2': 720,
'3': 1080
}
self.logMsg("Resolution is: %sP, transcode for resolution: %sP+"
log("Resolution is: %sP, transcode for resolution: %sP+"
% (resolution, res[transcodeH265]), 1)
if res[transcodeH265] <= resolution:
return False
@ -118,36 +126,35 @@ class PlayUtils():
canDirectPlay = item['MediaSources'][0]['SupportsDirectPlay']
# Make sure direct play is supported by the server
if not canDirectPlay:
self.logMsg("Can't direct play, server doesn't allow/support it.", 1)
log("Can't direct play, server doesn't allow/support it.", 1)
return False
location = item['LocationType']
if location == "FileSystem":
# Verify the path
if not self.fileExists():
self.logMsg("Unable to direct play.")
log("Unable to direct play.")
try:
count = int(utils.settings('failCount'))
count = int(settings('failCount'))
except ValueError:
count = 0
self.logMsg("Direct play failed: %s times." % count, 1)
log("Direct play failed: %s times." % count, 1)
if count < 2:
# Let the user know that direct play failed
utils.settings('failCount', value=str(count+1))
xbmcgui.Dialog().notification(
heading="Emby server",
message="Unable to direct play.",
settings('failCount', value=str(count+1))
dialog.notification(
heading="Emby for Kodi",
message=lang(33011),
icon="special://home/addons/plugin.video.emby/icon.png",
sound=False)
elif utils.settings('playFromStream') != "true":
elif settings('playFromStream') != "true":
# Permanently set direct stream as true
utils.settings('playFromStream', value="true")
utils.settings('failCount', value="0")
xbmcgui.Dialog().notification(
heading="Emby server",
message=("Direct play failed 3 times. Enabled play "
"from HTTP in the add-on settings."),
settings('playFromStream', value="true")
settings('failCount', value="0")
dialog.notification(
heading="Emby for Kodi",
message=lang(33012),
icon="special://home/addons/plugin.video.emby/icon.png",
sound=False)
return False
@ -185,28 +192,32 @@ class PlayUtils():
def fileExists(self):
log = self.logMsg
if 'Path' not in self.item:
# File has no path defined in server
return False
# Convert path to direct play
path = self.directPlay()
self.logMsg("Verifying path: %s" % path, 1)
log("Verifying path: %s" % path, 1)
if xbmcvfs.exists(path):
self.logMsg("Path exists.", 1)
log("Path exists.", 1)
return True
elif ":" not in path:
self.logMsg("Can't verify path, assumed linux. Still try to direct play.", 1)
log("Can't verify path, assumed linux. Still try to direct play.", 1)
return True
else:
self.logMsg("Failed to find file.")
log("Failed to find file.", 1)
return False
def isDirectStream(self):
log = self.logMsg
item = self.item
videotrack = item['MediaSources'][0]['Name']
@ -221,7 +232,7 @@ class PlayUtils():
'2': 720,
'3': 1080
}
self.logMsg("Resolution is: %sP, transcode for resolution: %sP+"
log("Resolution is: %sP, transcode for resolution: %sP+"
% (resolution, res[transcodeH265]), 1)
if res[transcodeH265] <= resolution:
return False
@ -234,7 +245,7 @@ class PlayUtils():
# Verify the bitrate
if not self.isNetworkSufficient():
self.logMsg("The network speed is insufficient to direct stream file.", 1)
log("The network speed is insufficient to direct stream file.", 1)
return False
return True
@ -245,12 +256,12 @@ class PlayUtils():
server = self.server
itemid = item['Id']
type = item['Type']
itemtype = item['Type']
if 'Path' in item and item['Path'].endswith('.strm'):
# Allow strm loading when direct streaming
playurl = self.directPlay()
elif type == "Audio":
elif itemtype == "Audio":
playurl = "%s/emby/Audio/%s/stream.mp3" % (server, itemid)
else:
playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
@ -259,14 +270,16 @@ class PlayUtils():
def isNetworkSufficient(self):
log = self.logMsg
settings = self.getBitrate()*1000
try:
sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
except (KeyError, TypeError):
self.logMsg("Bitrate value is missing.", 1)
log("Bitrate value is missing.", 1)
else:
self.logMsg("The add-on settings bitrate is: %s, the video bitrate required is: %s"
log("The add-on settings bitrate is: %s, the video bitrate required is: %s"
% (settings, sourceBitrate), 1)
if settings < sourceBitrate:
return False
@ -335,6 +348,10 @@ class PlayUtils():
return bitrate.get(videoQuality, 2147483)
def audioSubsPref(self, url, listitem):
log = self.logMsg
lang = utils.language
dialog = xbmcgui.Dialog()
# For transcoding only
# Present the list of audio to select from
audioStreamsList = {}
@ -373,8 +390,6 @@ class PlayUtils():
audioStreams.append(track)
elif 'Subtitle' in type:
'''if stream['IsExternal']:
continue'''
try:
track = "%s - %s" % (index, stream['Language'])
except:
@ -396,7 +411,7 @@ class PlayUtils():
if len(audioStreams) > 1:
resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
resp = dialog.select(lang(33013), audioStreams)
if resp > -1:
# User selected audio
selected = audioStreams[resp]
@ -409,7 +424,7 @@ class PlayUtils():
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
if len(subtitleStreams) > 1:
resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
resp = dialog.select(lang(33014), subtitleStreams)
if resp == 0:
# User selected no subtitles
pass
@ -424,7 +439,7 @@ class PlayUtils():
itemid = item['Id']
url = [("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
% (self.server, itemid, itemid, selectSubsIndex))]
self.logMsg("Set up subtitles: %s %s" % (selectSubsIndex, url), 1)
log("Set up subtitles: %s %s" % (selectSubsIndex, url), 1)
listitem.setSubtitles(url)
else:
# Burn subtitles

View file

@ -18,12 +18,14 @@ class Read_EmbyServer():
def __init__(self):
window = utils.window
self.clientInfo = clientinfo.ClientInfo()
self.addonName = self.clientInfo.getAddonName()
self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.userId = utils.window('emby_currUser')
self.server = utils.window('emby_server%s' % self.userId)
self.userId = window('emby_currUser')
self.server = window('emby_server%s' % self.userId)
def logMsg(self, msg, lvl=1):
@ -183,6 +185,8 @@ class Read_EmbyServer():
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
log = self.logMsg
doUtils = self.doUtils
items = {
@ -208,7 +212,7 @@ class Read_EmbyServer():
items['TotalRecordCount'] = total
except TypeError: # Failed to retrieve
self.logMsg("%s:%s Failed to retrieve the server response." % (url, params), 2)
log("%s:%s Failed to retrieve the server response." % (url, params), 2)
else:
index = 0
@ -250,27 +254,27 @@ class Read_EmbyServer():
# Something happened to the connection
if not throttled:
throttled = True
self.logMsg("Throttle activated.", 1)
log("Throttle activated.", 1)
if jump == highestjump:
# We already tried with the highestjump, but it failed. Reset value.
self.logMsg("Reset highest value.", 1)
log("Reset highest value.", 1)
highestjump = 0
# Lower the number by half
if highestjump:
throttled = False
jump = highestjump
self.logMsg("Throttle deactivated.", 1)
log("Throttle deactivated.", 1)
else:
jump = int(jump/4)
self.logMsg("Set jump limit to recover: %s" % jump, 1)
log("Set jump limit to recover: %s" % jump, 2)
retry = 0
while utils.window('emby_online') != "true":
# Wait server to come back online
if retry == 3:
self.logMsg("Unable to reconnect to server. Abort process.", 1)
log("Unable to reconnect to server. Abort process.", 1)
return
retry += 1
@ -298,7 +302,7 @@ class Read_EmbyServer():
increment = 10
jump += increment
self.logMsg("Increase jump limit to: %s" % jump, 1)
log("Increase jump limit to: %s" % jump, 1)
return items
def getViews(self, type, root=False):

View file

@ -81,42 +81,46 @@ class UserClient(threading.Thread):
def getUserId(self):
log = self.logMsg
window = utils.window
settings = utils.settings
username = self.getUsername()
w_userId = utils.window('emby_userId%s' % username)
s_userId = utils.settings('userId%s' % username)
w_userId = window('emby_currUser')
s_userId = settings('userId%s' % username)
# Verify the window property
if w_userId:
if not s_userId:
# Save access token if it's missing from settings
utils.settings('userId%s' % username, value=w_userId)
self.logMsg(
"Returning userId from WINDOW for username: %s UserId: %s"
settings('userId%s' % username, value=w_userId)
log("Returning userId from WINDOW for username: %s UserId: %s"
% (username, w_userId), 2)
return w_userId
# Verify the settings
elif s_userId:
self.logMsg(
"Returning userId from SETTINGS for username: %s userId: %s"
log("Returning userId from SETTINGS for username: %s userId: %s"
% (username, s_userId), 2)
return s_userId
# No userId found
else:
self.logMsg("No userId saved for username: %s." % username, 1)
log("No userId saved for username: %s." % username, 1)
def getServer(self, prefix=True):
alternate = utils.settings('altip') == "true"
settings = utils.settings
alternate = settings('altip') == "true"
if alternate:
# Alternate host
HTTPS = utils.settings('secondhttps') == "true"
host = utils.settings('secondipaddress')
port = utils.settings('secondport')
HTTPS = settings('secondhttps') == "true"
host = settings('secondipaddress')
port = settings('secondport')
else:
# Original host
HTTPS = utils.settings('https') == "true"
host = utils.settings('ipaddress')
port = utils.settings('port')
HTTPS = settings('https') == "true"
host = settings('ipaddress')
port = settings('port')
server = host + ":" + port
@ -138,35 +142,40 @@ class UserClient(threading.Thread):
def getToken(self):
log = self.logMsg
window = utils.window
settings = utils.settings
username = self.getUsername()
w_token = utils.window('emby_accessToken%s' % username)
s_token = utils.settings('accessToken')
userId = self.getUserId()
w_token = window('emby_accessToken%s' % userId)
s_token = settings('accessToken')
# Verify the window property
if w_token:
if not s_token:
# Save access token if it's missing from settings
utils.settings('accessToken', value=w_token)
self.logMsg(
"Returning accessToken from WINDOW for username: %s accessToken: %s"
settings('accessToken', value=w_token)
log("Returning accessToken from WINDOW for username: %s accessToken: %s"
% (username, w_token), 2)
return w_token
# Verify the settings
elif s_token:
self.logMsg(
"Returning accessToken from SETTINGS for username: %s accessToken: %s"
log("Returning accessToken from SETTINGS for username: %s accessToken: %s"
% (username, s_token), 2)
utils.window('emby_accessToken%s' % username, value=s_token)
window('emby_accessToken%s' % username, value=s_token)
return s_token
else:
self.logMsg("No token found.", 1)
log("No token found.", 1)
return ""
def getSSLverify(self):
# Verify host certificate
s_sslverify = utils.settings('sslverify')
if utils.settings('altip') == "true":
s_sslverify = utils.settings('secondsslverify')
settings = utils.settings
s_sslverify = settings('sslverify')
if settings('altip') == "true":
s_sslverify = settings('secondsslverify')
if s_sslverify == "true":
return True
@ -175,9 +184,11 @@ class UserClient(threading.Thread):
def getSSL(self):
# Client side certificate
s_cert = utils.settings('sslcert')
if utils.settings('altip') == "true":
s_cert = utils.settings('secondsslcert')
settings = utils.settings
s_cert = settings('sslcert')
if settings('altip') == "true":
s_cert = settings('secondsslcert')
if s_cert == "None":
return None
@ -186,11 +197,11 @@ class UserClient(threading.Thread):
def setUserPref(self):
doUtils = self.doUtils
doUtils = self.doUtils.downloadUrl
art = artwork.Artwork()
url = "{server}/emby/Users/{UserId}?format=json"
result = doUtils.downloadUrl(url)
result = doUtils(url)
self.userSettings = result
# Set user image for skin display
if result.get('PrimaryImageTag'):
@ -198,7 +209,7 @@ class UserClient(threading.Thread):
# Set resume point max
url = "{server}/emby/System/Configuration?format=json"
result = doUtils.downloadUrl(url)
result = doUtils(url)
utils.settings('markPlayed', value=str(result['MaxResumePct']))
@ -218,26 +229,31 @@ class UserClient(threading.Thread):
def hasAccess(self):
# hasAccess is verified in service.py
log = self.logMsg
window = utils.window
url = "{server}/emby/Users?format=json"
result = self.doUtils.downloadUrl(url)
if result == False:
# Access is restricted, set in downloadutils.py via exception
self.logMsg("Access is restricted.", 1)
log("Access is restricted.", 1)
self.HasAccess = False
elif utils.window('emby_online') != "true":
elif window('emby_online') != "true":
# Server connection failed
pass
elif utils.window('emby_serverStatus') == "restricted":
self.logMsg("Access is granted.", 1)
elif window('emby_serverStatus') == "restricted":
log("Access is granted.", 1)
self.HasAccess = True
utils.window('emby_serverStatus', clear=True)
xbmcgui.Dialog().notification("Emby server", "Access is enabled.")
window('emby_serverStatus', clear=True)
xbmcgui.Dialog().notification("Emby for Kodi", utils.language(33007))
def loadCurrUser(self, authenticated=False):
window = utils.window
doUtils = self.doUtils
username = self.getUsername()
userId = self.getUserId()
@ -252,8 +268,8 @@ class UserClient(threading.Thread):
# Test the validity of current token
if authenticated == False:
url = "%s/emby/Users/%s?format=json" % (self.currServer, userId)
utils.window('emby_currUser', value=userId)
utils.window('emby_accessToken%s' % userId, value=self.currToken)
window('emby_currUser', value=userId)
window('emby_accessToken%s' % userId, value=self.currToken)
result = doUtils.downloadUrl(url)
if result == 401:
@ -262,10 +278,10 @@ class UserClient(threading.Thread):
return False
# Set to windows property
utils.window('emby_currUser', value=userId)
utils.window('emby_accessToken%s' % userId, value=self.currToken)
utils.window('emby_server%s' % userId, value=self.currServer)
utils.window('emby_server_%s' % userId, value=self.getServer(prefix=False))
window('emby_currUser', value=userId)
window('emby_accessToken%s' % userId, value=self.currToken)
window('emby_server%s' % userId, value=self.currServer)
window('emby_server_%s' % userId, value=self.getServer(prefix=False))
# Set DownloadUtils values
doUtils.setUsername(username)
@ -284,6 +300,13 @@ class UserClient(threading.Thread):
def authenticate(self):
log = self.logMsg
lang = utils.language
window = utils.window
settings = utils.settings
dialog = xbmcgui.Dialog()
# Get /profile/addon_data
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
@ -293,12 +316,12 @@ class UserClient(threading.Thread):
# If there's no settings.xml
if not hasSettings:
self.logMsg("No settings.xml found.", 1)
log("No settings.xml found.", 1)
self.auth = False
return
# If no user information
elif not server or not username:
self.logMsg("Missing server information.", 1)
log("Missing server information.", 1)
self.auth = False
return
# If there's a token, load the user
@ -308,9 +331,9 @@ class UserClient(threading.Thread):
if result == False:
pass
else:
self.logMsg("Current user: %s" % self.currUser, 1)
self.logMsg("Current userId: %s" % self.currUserId, 1)
self.logMsg("Current accessToken: %s" % self.currToken, 2)
log("Current user: %s" % self.currUser, 1)
log("Current userId: %s" % self.currUserId, 1)
log("Current accessToken: %s" % self.currToken, 2)
return
##### AUTHENTICATE USER #####
@ -325,20 +348,20 @@ class UserClient(threading.Thread):
if username.decode('utf-8') in name:
# If user has password
if user['HasPassword'] == True:
password = xbmcgui.Dialog().input(
heading="Enter password for user: %s" % username,
password = dialog.input(
heading="%s %s" % (lang(33008), username),
option=xbmcgui.ALPHANUM_HIDE_INPUT)
# If password dialog is cancelled
if not password:
self.logMsg("No password entered.", 0)
utils.window('emby_serverStatus', value="Stop")
log("No password entered.", 0)
window('emby_serverStatus', value="Stop")
self.auth = False
return
break
else:
# Manual login, user is hidden
password = xbmcgui.Dialog().input(
heading="Enter password for user: %s" % username,
password = dialog.input(
heading="%s %s" % (lang(33008), username),
option=xbmcgui.ALPHANUM_HIDE_INPUT)
sha1 = hashlib.sha1(password)
sha1 = sha1.hexdigest()
@ -346,71 +369,72 @@ class UserClient(threading.Thread):
# Authenticate username and password
url = "%s/emby/Users/AuthenticateByName?format=json" % server
data = {'username': username, 'password': sha1}
self.logMsg(data, 2)
log(data, 2)
result = self.doUtils.downloadUrl(url, postBody=data, type="POST", authenticate=False)
try:
self.logMsg("Auth response: %s" % result, 1)
log("Auth response: %s" % result, 1)
accessToken = result['AccessToken']
except (KeyError, TypeError):
self.logMsg("Failed to retrieve the api key.", 1)
log("Failed to retrieve the api key.", 1)
accessToken = None
if accessToken is not None:
self.currUser = username
xbmcgui.Dialog().notification("Emby server", "Welcome %s!" % self.currUser)
dialog.notification("Emby for Kodi", "%s %s!" % (lang(33000), self.currUser), 1)
userId = result['User']['Id']
utils.settings('accessToken', value=accessToken)
utils.settings('userId%s' % username, value=userId)
self.logMsg("User Authenticated: %s" % accessToken, 1)
settings('accessToken', value=accessToken)
settings('userId%s' % username, value=userId)
log("User Authenticated: %s" % accessToken, 1)
self.loadCurrUser(authenticated=True)
utils.window('emby_serverStatus', clear=True)
window('emby_serverStatus', clear=True)
self.retry = 0
else:
self.logMsg("User authentication failed.", 1)
utils.settings('accessToken', value="")
utils.settings('userId%s' % username, value="")
xbmcgui.Dialog().ok("Error connecting", "Invalid username or password.")
log("User authentication failed.", 1)
settings('accessToken', value="")
settings('userId%s' % username, value="")
dialog.ok(lang(33001), lang(33009))
# Give two attempts at entering password
if self.retry == 2:
self.logMsg(
"""Too many retries. You can retry by resetting
attempts in the addon settings.""", 1)
utils.window('emby_serverStatus', value="Stop")
xbmcgui.Dialog().ok(
heading="Error connecting",
line1="Failed to authenticate too many times.",
line2="You can retry by resetting attempts in the addon settings.")
log("Too many retries. "
"You can retry by resetting attempts in the addon settings.", 1)
window('emby_serverStatus', value="Stop")
dialog.ok(lang(33001), lang(33010))
self.retry += 1
self.auth = False
def resetClient(self):
self.logMsg("Reset UserClient authentication.", 1)
username = self.getUsername()
log = self.logMsg
log("Reset UserClient authentication.", 1)
userId = self.getUserId()
if self.currToken is not None:
# In case of 401, removed saved token
utils.settings('accessToken', value="")
utils.window('emby_accessToken%s' % username, clear=True)
utils.window('emby_accessToken%s' % userId, clear=True)
self.currToken = None
self.logMsg("User token has been removed.", 1)
log("User token has been removed.", 1)
self.auth = True
self.currUser = None
def run(self):
log = self.logMsg
window = utils.window
monitor = xbmc.Monitor()
self.logMsg("----===## Starting UserClient ##===----", 0)
log("----===## Starting UserClient ##===----", 0)
while not monitor.abortRequested():
status = utils.window('emby_serverStatus')
status = window('emby_serverStatus')
if status:
# Verify the connection status to server
if status == "restricted":
@ -419,12 +443,12 @@ class UserClient(threading.Thread):
elif status == "401":
# Unauthorized access, revoke token
utils.window('emby_serverStatus', value="Auth")
window('emby_serverStatus', value="Auth")
self.resetClient()
if self.auth and (self.currUser is None):
# Try to authenticate user
status = utils.window('emby_serverStatus')
status = window('emby_serverStatus')
if not status or status == "Auth":
# Set auth flag because we no longer need
# to authenticate the user
@ -436,13 +460,13 @@ class UserClient(threading.Thread):
# If authenticate failed.
server = self.getServer()
username = self.getUsername()
status = utils.window('emby_serverStatus')
status = window('emby_serverStatus')
# The status Stop is for when user cancelled password dialog.
if server and username and status != "Stop":
# Only if there's information found to login
self.logMsg("Server found: %s" % server, 2)
self.logMsg("Username found: %s" % username, 2)
log("Server found: %s" % server, 2)
log("Username found: %s" % username, 2)
self.auth = True
@ -455,7 +479,7 @@ class UserClient(threading.Thread):
break
self.doUtils.stopSession()
self.logMsg("##===---- UserClient Stopped ----===##", 0)
log("##===---- UserClient Stopped ----===##", 0)
def stopClient(self):
# When emby for kodi terminates

View file

@ -23,7 +23,7 @@ class VideoNodes(object):
clientInfo = clientinfo.ClientInfo()
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):
@ -181,7 +181,7 @@ class VideoNodes(object):
nodeXML = "%s%s_%s.xml" % (nodepath, cleantagname, nodetype)
# Get label
stringid = nodes[node]
if node != '1':
if node != "1":
label = utils.language(stringid)
if not label:
label = xbmc.getLocalizedString(stringid)
@ -323,7 +323,7 @@ class VideoNodes(object):
cleantagname = utils.normalize_nodes(tagname)
nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
nodeXML = "%semby_%s.xml" % (nodepath, cleantagname)
path = "library://video/emby_%s.xml" % (cleantagname)
path = "library://video/emby_%s.xml" % cleantagname
windowpath = "ActivateWindow(Video,%s,return)" % path
# Create the video node directory

View file

@ -324,4 +324,4 @@ class WebSocket_Client(threading.Thread):
self.stopWebsocket = True
self.client.close()
self.logMsg("Stopping thread.")
self.logMsg("Stopping thread.", 1)

View file

@ -277,15 +277,15 @@ class Service():
##### Emby thread is terminating. #####
if self.userclient_running:
user.stopClient()
if self.library_running:
library.stopThread()
if self.websocket_running:
ws.stopClient()
if self.userclient_running:
user.stopClient()
log("======== STOP %s ========" % self.addonName, 0)
# Delay option