Update for strings
This commit is contained in:
parent
0ba1ac4104
commit
52a5d35295
10 changed files with 477 additions and 389 deletions
|
@ -47,7 +47,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
self.doUtils = downloadutils.DownloadUtils()
|
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||||
self.user = userclient.UserClient()
|
self.user = userclient.UserClient()
|
||||||
self.emby = embyserver.Read_EmbyServer()
|
self.emby = embyserver.Read_EmbyServer()
|
||||||
self.vnodes = videonodes.VideoNodes()
|
self.vnodes = videonodes.VideoNodes()
|
||||||
|
@ -72,17 +72,19 @@ class LibrarySync(threading.Thread):
|
||||||
return dialog
|
return dialog
|
||||||
|
|
||||||
def startSync(self):
|
def startSync(self):
|
||||||
|
|
||||||
|
settings = utils.settings
|
||||||
# Run at start up - optional to use the server plugin
|
# Run at start up - optional to use the server plugin
|
||||||
if utils.settings('SyncInstallRunDone') == "true":
|
if settings('SyncInstallRunDone') == "true":
|
||||||
|
|
||||||
# Validate views
|
# Validate views
|
||||||
self.refreshViews()
|
self.refreshViews()
|
||||||
completed = False
|
completed = False
|
||||||
# Verify if server plugin is installed.
|
# Verify if server plugin is installed.
|
||||||
if utils.settings('serverSync') == "true":
|
if settings('serverSync') == "true":
|
||||||
# Try to use fast start up
|
# Try to use fast start up
|
||||||
url = "{server}/emby/Plugins?format=json"
|
url = "{server}/emby/Plugins?format=json"
|
||||||
result = self.doUtils.downloadUrl(url)
|
result = self.doUtils(url)
|
||||||
|
|
||||||
for plugin in result:
|
for plugin in result:
|
||||||
if plugin['Name'] == "Emby.Kodi Sync Queue":
|
if plugin['Name'] == "Emby.Kodi Sync Queue":
|
||||||
|
@ -100,30 +102,43 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
def fastSync(self):
|
def fastSync(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
|
doUtils = self.doUtils
|
||||||
|
|
||||||
lastSync = utils.settings('LastIncrementalSync')
|
lastSync = utils.settings('LastIncrementalSync')
|
||||||
if not lastSync:
|
if not lastSync:
|
||||||
lastSync = "2010-01-01T00:00:00Z"
|
lastSync = "2010-01-01T00:00:00Z"
|
||||||
|
|
||||||
lastSyncTime = utils.convertdate(lastSync)
|
lastSyncTime = utils.convertdate(lastSync)
|
||||||
self.logMsg("Last sync run: %s" % lastSyncTime, 1)
|
log("Last sync run: %s" % lastSyncTime, 1)
|
||||||
|
|
||||||
# get server RetentionDateTime
|
# get server RetentionDateTime
|
||||||
url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
|
url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
|
||||||
result = self.doUtils.downloadUrl(url)
|
result = doUtils(url)
|
||||||
retention_time = "2010-01-01T00:00:00Z"
|
retention_time = "2010-01-01T00:00:00Z"
|
||||||
if result and result.get('RetentionDateTime'):
|
if result and result.get('RetentionDateTime'):
|
||||||
retention_time = result['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)
|
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 last sync before retention time do a full sync
|
||||||
if retention_time > lastSyncTime:
|
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
|
return False
|
||||||
|
|
||||||
url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json"
|
url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json"
|
||||||
params = {'LastUpdateDT': lastSync}
|
params = {'LastUpdateDT': lastSync}
|
||||||
result = self.doUtils.downloadUrl(url, parameters=params)
|
result = doUtils(url, parameters=params)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
processlist = {
|
processlist = {
|
||||||
|
@ -135,36 +150,38 @@ class LibrarySync(threading.Thread):
|
||||||
}
|
}
|
||||||
|
|
||||||
except (KeyError, TypeError):
|
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
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg("Fast sync changes: %s" % result, 1)
|
log("Fast sync changes: %s" % result, 1)
|
||||||
for action in processlist:
|
for action in processlist:
|
||||||
self.triage_items(action, processlist[action])
|
self.triage_items(action, processlist[action])
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveLastSync(self):
|
def saveLastSync(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
# Save last sync time
|
# Save last sync time
|
||||||
overlap = 2
|
overlap = 2
|
||||||
|
|
||||||
url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
|
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
|
try: # datetime fails when used more than once, TypeError
|
||||||
server_time = result['ServerDateTime']
|
server_time = result['ServerDateTime']
|
||||||
server_time = utils.convertdate(server_time)
|
server_time = utils.convertdate(server_time)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If the server plugin is not installed or an error happened.
|
# 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)
|
time_now = datetime.utcnow()-timedelta(minutes=overlap)
|
||||||
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
|
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:
|
else:
|
||||||
lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
|
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:
|
finally:
|
||||||
utils.settings('LastIncrementalSync', value=lastSync)
|
utils.settings('LastIncrementalSync', value=lastSync)
|
||||||
|
@ -179,32 +196,39 @@ class LibrarySync(threading.Thread):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def dbCommit(self, connection):
|
def dbCommit(self, connection):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
# Central commit, verifies if Kodi database update is running
|
# 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:
|
while kodidb_scan:
|
||||||
|
|
||||||
self.logMsg("Kodi scan is running. Waiting...", 1)
|
log("Kodi scan is running. Waiting...", 1)
|
||||||
kodidb_scan = utils.window('emby_kodiScan') == "true"
|
kodidb_scan = window('emby_kodiScan') == "true"
|
||||||
|
|
||||||
if self.shouldStop():
|
if self.shouldStop():
|
||||||
self.logMsg("Commit unsuccessful. Sync terminated.", 1)
|
log("Commit unsuccessful. Sync terminated.", 1)
|
||||||
break
|
break
|
||||||
|
|
||||||
if self.monitor.waitForAbort(1):
|
if self.monitor.waitForAbort(1):
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
self.logMsg("Commit unsuccessful.", 1)
|
log("Commit unsuccessful.", 1)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
connection.commit()
|
connection.commit()
|
||||||
self.logMsg("Commit successful.", 1)
|
log("Commit successful.", 1)
|
||||||
|
|
||||||
def fullSync(self, manualrun=False, repair=False):
|
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.
|
# Only run once when first setting up. Can be run manually.
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
music_enabled = utils.settings('enableMusic') == "true"
|
music_enabled = utils.settings('enableMusic') == "true"
|
||||||
|
|
||||||
utils.window('emby_dbScan', value="true")
|
window('emby_dbScan', value="true")
|
||||||
# Add sources
|
# Add sources
|
||||||
utils.sourcesXML()
|
utils.sourcesXML()
|
||||||
|
|
||||||
|
@ -232,7 +256,7 @@ class LibrarySync(threading.Thread):
|
||||||
message = "Repair sync"
|
message = "Repair sync"
|
||||||
else:
|
else:
|
||||||
message = "Initial sync"
|
message = "Initial sync"
|
||||||
utils.window('emby_initialScan', value="true")
|
window('emby_initialScan', value="true")
|
||||||
|
|
||||||
pDialog = self.progressDialog("%s" % message, forced=True)
|
pDialog = self.progressDialog("%s" % message, forced=True)
|
||||||
starttotal = datetime.now()
|
starttotal = datetime.now()
|
||||||
|
@ -253,7 +277,7 @@ class LibrarySync(threading.Thread):
|
||||||
completed = process[itemtype](embycursor, kodicursor, pDialog)
|
completed = process[itemtype](embycursor, kodicursor, pDialog)
|
||||||
if not completed:
|
if not completed:
|
||||||
|
|
||||||
utils.window('emby_dbScan', clear=True)
|
window('emby_dbScan', clear=True)
|
||||||
if pDialog:
|
if pDialog:
|
||||||
pDialog.close()
|
pDialog.close()
|
||||||
|
|
||||||
|
@ -264,8 +288,7 @@ class LibrarySync(threading.Thread):
|
||||||
self.dbCommit(kodiconn)
|
self.dbCommit(kodiconn)
|
||||||
embyconn.commit()
|
embyconn.commit()
|
||||||
elapsedTime = datetime.now() - startTime
|
elapsedTime = datetime.now() - startTime
|
||||||
self.logMsg(
|
log("SyncDatabase (finished %s in: %s)"
|
||||||
"SyncDatabase (finished %s in: %s)"
|
|
||||||
% (itemtype, str(elapsedTime).split('.')[0]), 1)
|
% (itemtype, str(elapsedTime).split('.')[0]), 1)
|
||||||
else:
|
else:
|
||||||
# Close the Kodi cursor
|
# Close the Kodi cursor
|
||||||
|
@ -281,7 +304,7 @@ class LibrarySync(threading.Thread):
|
||||||
completed = self.music(embycursor, musiccursor, pDialog)
|
completed = self.music(embycursor, musiccursor, pDialog)
|
||||||
if not completed:
|
if not completed:
|
||||||
|
|
||||||
utils.window('emby_dbScan', clear=True)
|
window('emby_dbScan', clear=True)
|
||||||
if pDialog:
|
if pDialog:
|
||||||
pDialog.close()
|
pDialog.close()
|
||||||
|
|
||||||
|
@ -292,8 +315,7 @@ class LibrarySync(threading.Thread):
|
||||||
musicconn.commit()
|
musicconn.commit()
|
||||||
embyconn.commit()
|
embyconn.commit()
|
||||||
elapsedTime = datetime.now() - startTime
|
elapsedTime = datetime.now() - startTime
|
||||||
self.logMsg(
|
log("SyncDatabase (finished music in: %s)"
|
||||||
"SyncDatabase (finished music in: %s)"
|
|
||||||
% (str(elapsedTime).split('.')[0]), 1)
|
% (str(elapsedTime).split('.')[0]), 1)
|
||||||
musiccursor.close()
|
musiccursor.close()
|
||||||
|
|
||||||
|
@ -302,18 +324,18 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
embycursor.close()
|
embycursor.close()
|
||||||
|
|
||||||
utils.settings('SyncInstallRunDone', value="true")
|
settings('SyncInstallRunDone', value="true")
|
||||||
utils.settings("dbCreatedWithVersion", self.clientInfo.getVersion())
|
settings("dbCreatedWithVersion", self.clientInfo.getVersion())
|
||||||
self.saveLastSync()
|
self.saveLastSync()
|
||||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||||
elapsedtotal = datetime.now() - starttotal
|
elapsedtotal = datetime.now() - starttotal
|
||||||
|
|
||||||
utils.window('emby_dbScan', clear=True)
|
window('emby_dbScan', clear=True)
|
||||||
utils.window('emby_initialScan', clear=True)
|
window('emby_initialScan', clear=True)
|
||||||
xbmcgui.Dialog().notification(
|
xbmcgui.Dialog().notification(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="%s completed in: %s" %
|
message="%s %s %s" %
|
||||||
(message, str(elapsedtotal).split('.')[0]),
|
(message, utils.language(33025), str(elapsedtotal).split('.')[0]),
|
||||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||||
sound=False)
|
sound=False)
|
||||||
return True
|
return True
|
||||||
|
@ -336,6 +358,8 @@ class LibrarySync(threading.Thread):
|
||||||
embycursor.close()
|
embycursor.close()
|
||||||
|
|
||||||
def maintainViews(self, embycursor, kodicursor):
|
def maintainViews(self, embycursor, kodicursor):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
# Compare the views to emby
|
# Compare the views to emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
kodi_db = kodidb.Kodidb_Functions(kodicursor)
|
kodi_db = kodidb.Kodidb_Functions(kodicursor)
|
||||||
|
@ -344,14 +368,14 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
# Get views
|
# Get views
|
||||||
url = "{server}/emby/Users/{UserId}/Views?format=json"
|
url = "{server}/emby/Users/{UserId}/Views?format=json"
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils(url)
|
||||||
grouped_views = result['Items']
|
grouped_views = result['Items']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
groupedFolders = self.user.userSettings['Configuration']['GroupedFolders']
|
groupedFolders = self.user.userSettings['Configuration']['GroupedFolders']
|
||||||
except TypeError:
|
except TypeError:
|
||||||
url = "{server}/emby/Users/{UserId}?format=json"
|
url = "{server}/emby/Users/{UserId}?format=json"
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils(url)
|
||||||
groupedFolders = result['Configuration']['GroupedFolders']
|
groupedFolders = result['Configuration']['GroupedFolders']
|
||||||
|
|
||||||
# total nodes for window properties
|
# total nodes for window properties
|
||||||
|
@ -402,7 +426,7 @@ class LibrarySync(threading.Thread):
|
||||||
emby_db.addView(folderid, foldername, viewtype, tagid)
|
emby_db.addView(folderid, foldername, viewtype, tagid)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg(' '.join((
|
log(' '.join((
|
||||||
|
|
||||||
"Found viewid: %s" % folderid,
|
"Found viewid: %s" % folderid,
|
||||||
"viewname: %s" % current_viewname,
|
"viewname: %s" % current_viewname,
|
||||||
|
@ -411,7 +435,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
# View was modified, update with latest info
|
# View was modified, update with latest info
|
||||||
if current_viewname != foldername:
|
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)
|
tagid = kodi_db.createTag(foldername)
|
||||||
|
|
||||||
# Update view with new info
|
# Update view with new info
|
||||||
|
@ -466,6 +490,9 @@ class LibrarySync(threading.Thread):
|
||||||
utils.window('Emby.nodes.total', str(totalnodes))
|
utils.window('Emby.nodes.total', str(totalnodes))
|
||||||
|
|
||||||
def movies(self, embycursor, kodicursor, pdialog):
|
def movies(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
# Get movies from emby
|
# Get movies from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
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('movies')
|
||||||
views += emby_db.getView_byType('mixed')
|
views += emby_db.getView_byType('mixed')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
##### PROCESS MOVIES #####
|
##### PROCESS MOVIES #####
|
||||||
for view in views:
|
for view in views:
|
||||||
|
@ -488,7 +515,7 @@ class LibrarySync(threading.Thread):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="Gathering movies from view: %s..." % viewName)
|
message="%s %s..." % (lang(33017), viewName))
|
||||||
|
|
||||||
# Initial or repair sync
|
# Initial or repair sync
|
||||||
all_embymovies = emby.getMovies(viewId, dialog=pdialog)
|
all_embymovies = emby.getMovies(viewId, dialog=pdialog)
|
||||||
|
@ -511,12 +538,12 @@ class LibrarySync(threading.Thread):
|
||||||
count += 1
|
count += 1
|
||||||
movies.add_update(embymovie, viewName, viewId)
|
movies.add_update(embymovie, viewName, viewId)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Movies finished.", 2)
|
log("Movies finished.", 2)
|
||||||
|
|
||||||
|
|
||||||
##### PROCESS BOXSETS #####
|
##### PROCESS BOXSETS #####
|
||||||
if pdialog:
|
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)
|
boxsets = emby.getBoxset(dialog=pdialog)
|
||||||
total = boxsets['TotalRecordCount']
|
total = boxsets['TotalRecordCount']
|
||||||
|
@ -538,18 +565,20 @@ class LibrarySync(threading.Thread):
|
||||||
count += 1
|
count += 1
|
||||||
movies.add_updateBoxset(boxset)
|
movies.add_updateBoxset(boxset)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Boxsets finished.", 2)
|
log("Boxsets finished.", 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def musicvideos(self, embycursor, kodicursor, pdialog):
|
def musicvideos(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
# Get musicvideos from emby
|
# Get musicvideos from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
||||||
|
|
||||||
views = emby_db.getView_byType('musicvideos')
|
views = emby_db.getView_byType('musicvideos')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
for view in views:
|
for view in views:
|
||||||
|
|
||||||
|
@ -563,7 +592,7 @@ class LibrarySync(threading.Thread):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="Gathering musicvideos from view: %s..." % viewName)
|
message="%s %s..." % (utils.language(33019), viewName))
|
||||||
|
|
||||||
# Initial or repair sync
|
# Initial or repair sync
|
||||||
all_embymvideos = emby.getMusicVideos(viewId, dialog=pdialog)
|
all_embymvideos = emby.getMusicVideos(viewId, dialog=pdialog)
|
||||||
|
@ -586,11 +615,13 @@ class LibrarySync(threading.Thread):
|
||||||
count += 1
|
count += 1
|
||||||
mvideos.add_update(embymvideo, viewName, viewId)
|
mvideos.add_update(embymvideo, viewName, viewId)
|
||||||
else:
|
else:
|
||||||
self.logMsg("MusicVideos finished.", 2)
|
log("MusicVideos finished.", 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def tvshows(self, embycursor, kodicursor, pdialog):
|
def tvshows(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
# Get shows from emby
|
# Get shows from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
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('tvshows')
|
||||||
views += emby_db.getView_byType('mixed')
|
views += emby_db.getView_byType('mixed')
|
||||||
self.logMsg("Media folders: %s" % views, 1)
|
log("Media folders: %s" % views, 1)
|
||||||
|
|
||||||
for view in views:
|
for view in views:
|
||||||
|
|
||||||
|
@ -612,7 +643,7 @@ class LibrarySync(threading.Thread):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
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)
|
all_embytvshows = emby.getShows(viewId, dialog=pdialog)
|
||||||
total = all_embytvshows['TotalRecordCount']
|
total = all_embytvshows['TotalRecordCount']
|
||||||
|
@ -648,7 +679,7 @@ class LibrarySync(threading.Thread):
|
||||||
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
|
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
|
||||||
tvshows.add_updateEpisode(episode)
|
tvshows.add_updateEpisode(episode)
|
||||||
else:
|
else:
|
||||||
self.logMsg("TVShows finished.", 2)
|
log("TVShows finished.", 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -665,19 +696,19 @@ class LibrarySync(threading.Thread):
|
||||||
'songs': [emby.getSongs, music.add_updateSong]
|
'songs': [emby.getSongs, music.add_updateSong]
|
||||||
}
|
}
|
||||||
types = ['artists', 'albums', 'songs']
|
types = ['artists', 'albums', 'songs']
|
||||||
for type in types:
|
for itemtype in types:
|
||||||
|
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
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']
|
total = all_embyitems['TotalRecordCount']
|
||||||
embyitems = all_embyitems['Items']
|
embyitems = all_embyitems['Items']
|
||||||
|
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(heading="Processing %s / %s items" % (type, total))
|
pdialog.update(heading="Processing %s / %s items" % (itemtype, total))
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for embyitem in embyitems:
|
for embyitem in embyitems:
|
||||||
|
@ -691,9 +722,9 @@ class LibrarySync(threading.Thread):
|
||||||
pdialog.update(percentage, message=title)
|
pdialog.update(percentage, message=title)
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
process[type][1](embyitem)
|
process[itemtype][1](embyitem)
|
||||||
else:
|
else:
|
||||||
self.logMsg("%s finished." % type, 2)
|
self.logMsg("%s finished." % itemtype, 2)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -719,6 +750,8 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
def incrementalSync(self):
|
def incrementalSync(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
embyconn = utils.kodiSQL('emby')
|
embyconn = utils.kodiSQL('emby')
|
||||||
embycursor = embyconn.cursor()
|
embycursor = embyconn.cursor()
|
||||||
kodiconn = utils.kodiSQL('video')
|
kodiconn = utils.kodiSQL('video')
|
||||||
|
@ -788,7 +821,7 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
if update_embydb:
|
if update_embydb:
|
||||||
update_embydb = False
|
update_embydb = False
|
||||||
self.logMsg("Updating emby database.", 1)
|
log("Updating emby database.", 1)
|
||||||
embyconn.commit()
|
embyconn.commit()
|
||||||
self.saveLastSync()
|
self.saveLastSync()
|
||||||
|
|
||||||
|
@ -797,7 +830,7 @@ class LibrarySync(threading.Thread):
|
||||||
self.forceLibraryUpdate = False
|
self.forceLibraryUpdate = False
|
||||||
self.dbCommit(kodiconn)
|
self.dbCommit(kodiconn)
|
||||||
|
|
||||||
self.logMsg("Updating video library.", 1)
|
log("Updating video library.", 1)
|
||||||
utils.window('emby_kodiScan', value="true")
|
utils.window('emby_kodiScan', value="true")
|
||||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||||
|
|
||||||
|
@ -839,10 +872,16 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
def run_internal(self):
|
def run_internal(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
|
window = utils.window
|
||||||
|
settings = utils.settings
|
||||||
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
||||||
startupComplete = False
|
startupComplete = False
|
||||||
monitor = self.monitor
|
monitor = self.monitor
|
||||||
|
|
||||||
self.logMsg("---===### Starting LibrarySync ###===---", 0)
|
log("---===### Starting LibrarySync ###===---", 0)
|
||||||
|
|
||||||
while not monitor.abortRequested():
|
while not monitor.abortRequested():
|
||||||
|
|
||||||
|
@ -853,36 +892,24 @@ class LibrarySync(threading.Thread):
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
break
|
break
|
||||||
|
|
||||||
if (utils.window('emby_dbCheck') != "true" and
|
if (window('emby_dbCheck') != "true" and settings('SyncInstallRunDone') == "true"):
|
||||||
utils.settings('SyncInstallRunDone') == "true"):
|
|
||||||
|
|
||||||
# Verify the validity of the database
|
# Verify the validity of the database
|
||||||
currentVersion = utils.settings('dbCreatedWithVersion')
|
currentVersion = settings('dbCreatedWithVersion')
|
||||||
minVersion = utils.window('emby_minDBVersion')
|
minVersion = window('emby_minDBVersion')
|
||||||
uptoDate = self.compareDBVersion(currentVersion, minVersion)
|
uptoDate = self.compareDBVersion(currentVersion, minVersion)
|
||||||
|
|
||||||
if not uptoDate:
|
if not uptoDate:
|
||||||
self.logMsg(
|
log("Database version out of date: %s minimum version required: %s"
|
||||||
"Db version out of date: %s minimum version required: %s"
|
|
||||||
% (currentVersion, minVersion), 0)
|
% (currentVersion, minVersion), 0)
|
||||||
|
|
||||||
resp = xbmcgui.Dialog().yesno(
|
resp = dialog.yesno("Emby for Kodi", lang(33022))
|
||||||
heading="Db Version",
|
|
||||||
line1=(
|
|
||||||
"Detected the database needs to be "
|
|
||||||
"recreated for this version of Emby for Kodi. "
|
|
||||||
"Proceed?"))
|
|
||||||
if not resp:
|
if not resp:
|
||||||
self.logMsg("Db version out of date! USER IGNORED!", 0)
|
log("Database version is out of date! USER IGNORED!", 0)
|
||||||
xbmcgui.Dialog().ok(
|
dialog.ok("Emby for Kodi", lang(33023))
|
||||||
heading="Emby for Kodi",
|
|
||||||
line1=(
|
|
||||||
"Emby for Kodi may not work correctly "
|
|
||||||
"until the database is reset."))
|
|
||||||
else:
|
else:
|
||||||
utils.reset()
|
utils.reset()
|
||||||
|
|
||||||
utils.window('emby_dbCheck', value="true")
|
window('emby_dbCheck', value="true")
|
||||||
|
|
||||||
|
|
||||||
if not startupComplete:
|
if not startupComplete:
|
||||||
|
@ -890,58 +917,52 @@ class LibrarySync(threading.Thread):
|
||||||
videoDb = utils.getKodiVideoDBPath()
|
videoDb = utils.getKodiVideoDBPath()
|
||||||
if not xbmcvfs.exists(videoDb):
|
if not xbmcvfs.exists(videoDb):
|
||||||
# Database does not exists
|
# Database does not exists
|
||||||
self.logMsg(
|
log(
|
||||||
"The current Kodi version is incompatible "
|
"The current Kodi version is incompatible "
|
||||||
"with the Emby for Kodi add-on. Please visit "
|
"with the Emby for Kodi add-on. Please visit "
|
||||||
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
|
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
|
||||||
"to know which Kodi versions are supported.", 0)
|
"to know which Kodi versions are supported.", 0)
|
||||||
|
|
||||||
xbmcgui.Dialog().ok(
|
dialog.ok(
|
||||||
heading="Emby Warning",
|
heading="Emby for Kodi",
|
||||||
line1=(
|
line1=lang(33024))
|
||||||
"Cancelling the database syncing process. "
|
|
||||||
"Current Kodi versoin: %s is unsupported. "
|
|
||||||
"Please verify your logs for more info."
|
|
||||||
% xbmc.getInfoLabel('System.BuildVersion')))
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# Run start up sync
|
# Run start up sync
|
||||||
self.logMsg("Db version: %s" % utils.settings('dbCreatedWithVersion'), 0)
|
log("Database version: %s" % settings('dbCreatedWithVersion'), 0)
|
||||||
self.logMsg("SyncDatabase (started)", 1)
|
log("SyncDatabase (started)", 1)
|
||||||
startTime = datetime.now()
|
startTime = datetime.now()
|
||||||
librarySync = self.startSync()
|
librarySync = self.startSync()
|
||||||
elapsedTime = datetime.now() - startTime
|
elapsedTime = datetime.now() - startTime
|
||||||
self.logMsg(
|
log("SyncDatabase (finished in: %s) %s"
|
||||||
"SyncDatabase (finished in: %s) %s"
|
|
||||||
% (str(elapsedTime).split('.')[0], librarySync), 1)
|
% (str(elapsedTime).split('.')[0], librarySync), 1)
|
||||||
# Only try the initial sync once per kodi session regardless
|
# Only try the initial sync once per kodi session regardless
|
||||||
# This will prevent an infinite loop in case something goes wrong.
|
# This will prevent an infinite loop in case something goes wrong.
|
||||||
startupComplete = True
|
startupComplete = True
|
||||||
|
|
||||||
# Process updates
|
# Process updates
|
||||||
if utils.window('emby_dbScan') != "true":
|
if window('emby_dbScan') != "true":
|
||||||
self.incrementalSync()
|
self.incrementalSync()
|
||||||
|
|
||||||
if (utils.window('emby_onWake') == "true" and
|
if window('emby_onWake') == "true" and window('emby_online') == "true":
|
||||||
utils.window('emby_online') == "true"):
|
|
||||||
# Kodi is waking up
|
# Kodi is waking up
|
||||||
# Set in kodimonitor.py
|
# Set in kodimonitor.py
|
||||||
utils.window('emby_onWake', clear=True)
|
window('emby_onWake', clear=True)
|
||||||
if utils.window('emby_syncRunning') != "true":
|
if window('emby_syncRunning') != "true":
|
||||||
self.logMsg("SyncDatabase onWake (started)", 0)
|
log("SyncDatabase onWake (started)", 0)
|
||||||
librarySync = self.startSync()
|
librarySync = self.startSync()
|
||||||
self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
log("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
||||||
|
|
||||||
if self.stop_thread:
|
if self.stop_thread:
|
||||||
# Set in service.py
|
# Set in service.py
|
||||||
self.logMsg("Service terminated thread.", 2)
|
log("Service terminated thread.", 2)
|
||||||
break
|
break
|
||||||
|
|
||||||
if monitor.waitForAbort(1):
|
if monitor.waitForAbort(1):
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
break
|
break
|
||||||
|
|
||||||
self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
|
log("###===--- LibrarySync Stopped ---===###", 0)
|
||||||
|
|
||||||
def stopThread(self):
|
def stopThread(self):
|
||||||
self.stop_thread = True
|
self.stop_thread = True
|
||||||
|
@ -966,6 +987,9 @@ class ManualSync(LibrarySync):
|
||||||
|
|
||||||
|
|
||||||
def movies(self, embycursor, kodicursor, pdialog):
|
def movies(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
# Get movies from emby
|
# Get movies from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
|
@ -973,7 +997,7 @@ class ManualSync(LibrarySync):
|
||||||
|
|
||||||
views = emby_db.getView_byType('movies')
|
views = emby_db.getView_byType('movies')
|
||||||
views += emby_db.getView_byType('mixed')
|
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
|
# Pull the list of movies and boxsets in Kodi
|
||||||
try:
|
try:
|
||||||
|
@ -1003,7 +1027,7 @@ class ManualSync(LibrarySync):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
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)
|
all_embymovies = emby.getMovies(viewId, basic=True, dialog=pdialog)
|
||||||
for embymovie in all_embymovies['Items']:
|
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
|
# Only update if movie is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
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)
|
embymovies = emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1047,9 +1071,7 @@ class ManualSync(LibrarySync):
|
||||||
embyboxsets = []
|
embyboxsets = []
|
||||||
|
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update("Emby for Kodi", lang(33027))
|
||||||
heading="Emby for Kodi",
|
|
||||||
message="Comparing boxsets...")
|
|
||||||
|
|
||||||
for boxset in boxsets['Items']:
|
for boxset in boxsets['Items']:
|
||||||
|
|
||||||
|
@ -1066,7 +1088,7 @@ class ManualSync(LibrarySync):
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
embyboxsets.append(boxset)
|
embyboxsets.append(boxset)
|
||||||
|
|
||||||
self.logMsg("Boxsets to update: %s" % updatelist, 1)
|
log("Boxsets to update: %s" % updatelist, 1)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
|
|
||||||
if pdialog:
|
if pdialog:
|
||||||
|
@ -1091,24 +1113,26 @@ class ManualSync(LibrarySync):
|
||||||
if kodimovie not in all_embymoviesIds:
|
if kodimovie not in all_embymoviesIds:
|
||||||
movies.remove(kodimovie)
|
movies.remove(kodimovie)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Movies compare finished.", 1)
|
log("Movies compare finished.", 1)
|
||||||
|
|
||||||
for boxset in all_kodisets:
|
for boxset in all_kodisets:
|
||||||
if boxset not in all_embyboxsetsIds:
|
if boxset not in all_embyboxsetsIds:
|
||||||
movies.remove(boxset)
|
movies.remove(boxset)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Boxsets compare finished.", 1)
|
log("Boxsets compare finished.", 1)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def musicvideos(self, embycursor, kodicursor, pdialog):
|
def musicvideos(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
# Get musicvideos from emby
|
# Get musicvideos from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
||||||
|
|
||||||
views = emby_db.getView_byType('musicvideos')
|
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
|
# Pull the list of musicvideos in Kodi
|
||||||
try:
|
try:
|
||||||
|
@ -1131,7 +1155,7 @@ class ManualSync(LibrarySync):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
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)
|
all_embymvideos = emby.getMusicVideos(viewId, basic=True, dialog=pdialog)
|
||||||
for embymvideo in all_embymvideos['Items']:
|
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
|
# Only update if musicvideo is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
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)
|
embymvideos = emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1176,11 +1200,14 @@ class ManualSync(LibrarySync):
|
||||||
if kodimvideo not in all_embymvideosIds:
|
if kodimvideo not in all_embymvideosIds:
|
||||||
mvideos.remove(kodimvideo)
|
mvideos.remove(kodimvideo)
|
||||||
else:
|
else:
|
||||||
self.logMsg("MusicVideos compare finished.", 1)
|
log("MusicVideos compare finished.", 1)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def tvshows(self, embycursor, kodicursor, pdialog):
|
def tvshows(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
# Get shows from emby
|
# Get shows from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
|
@ -1188,7 +1215,7 @@ class ManualSync(LibrarySync):
|
||||||
|
|
||||||
views = emby_db.getView_byType('tvshows')
|
views = emby_db.getView_byType('tvshows')
|
||||||
views += emby_db.getView_byType('mixed')
|
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
|
# Pull the list of tvshows and episodes in Kodi
|
||||||
try:
|
try:
|
||||||
|
@ -1218,7 +1245,7 @@ class ManualSync(LibrarySync):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
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)
|
all_embytvshows = emby.getShows(viewId, basic=True, dialog=pdialog)
|
||||||
for embytvshow in all_embytvshows['Items']:
|
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
|
# Only update if movie is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
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)
|
embytvshows = emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1263,7 +1290,7 @@ class ManualSync(LibrarySync):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
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)
|
all_embyepisodes = emby.getEpisodes(viewId, basic=True, dialog=pdialog)
|
||||||
for embyepisode in all_embyepisodes['Items']:
|
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
|
# Only update if movie is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
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)
|
embyepisodes = emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1305,17 +1332,19 @@ class ManualSync(LibrarySync):
|
||||||
if koditvshow not in all_embytvshowsIds:
|
if koditvshow not in all_embytvshowsIds:
|
||||||
tvshows.remove(koditvshow)
|
tvshows.remove(koditvshow)
|
||||||
else:
|
else:
|
||||||
self.logMsg("TVShows compare finished.", 1)
|
log("TVShows compare finished.", 1)
|
||||||
|
|
||||||
for kodiepisode in all_kodiepisodes:
|
for kodiepisode in all_kodiepisodes:
|
||||||
if kodiepisode not in all_embyepisodesIds:
|
if kodiepisode not in all_embyepisodesIds:
|
||||||
tvshows.remove(kodiepisode)
|
tvshows.remove(kodiepisode)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Episodes compare finished.", 1)
|
log("Episodes compare finished.", 1)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def music(self, embycursor, kodicursor, pdialog):
|
def music(self, embycursor, kodicursor, pdialog):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
# Get music from emby
|
# Get music from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
|
@ -1354,7 +1383,7 @@ class ManualSync(LibrarySync):
|
||||||
if pdialog:
|
if pdialog:
|
||||||
pdialog.update(
|
pdialog.update(
|
||||||
heading="Emby for Kodi",
|
heading="Emby for Kodi",
|
||||||
message="Comparing %s..." % type)
|
message="%s %s..." % (utils.language(33031), type))
|
||||||
|
|
||||||
if type != "artists":
|
if type != "artists":
|
||||||
all_embyitems = process[type][0](basic=True, dialog=pdialog)
|
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
|
# Only update if songs is not in Kodi or checksum is different
|
||||||
updatelist.append(itemid)
|
updatelist.append(itemid)
|
||||||
|
|
||||||
self.logMsg("%s to update: %s" % (type, updatelist), 1)
|
log("%s to update: %s" % (type, updatelist), 1)
|
||||||
embyitems = emby.getFullItems(updatelist)
|
embyitems = emby.getFullItems(updatelist)
|
||||||
total = len(updatelist)
|
total = len(updatelist)
|
||||||
del updatelist[:]
|
del updatelist[:]
|
||||||
|
@ -1411,18 +1440,18 @@ class ManualSync(LibrarySync):
|
||||||
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
|
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
|
||||||
music.remove(kodiartist)
|
music.remove(kodiartist)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Artist compare finished.", 1)
|
log("Artist compare finished.", 1)
|
||||||
|
|
||||||
for kodialbum in all_kodialbums:
|
for kodialbum in all_kodialbums:
|
||||||
if kodialbum not in all_embyalbumsIds:
|
if kodialbum not in all_embyalbumsIds:
|
||||||
music.remove(kodialbum)
|
music.remove(kodialbum)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Albums compare finished.", 1)
|
log("Albums compare finished.", 1)
|
||||||
|
|
||||||
for kodisong in all_kodisongs:
|
for kodisong in all_kodisongs:
|
||||||
if kodisong not in all_embysongsIds:
|
if kodisong not in all_embysongsIds:
|
||||||
music.remove(kodisong)
|
music.remove(kodisong)
|
||||||
else:
|
else:
|
||||||
self.logMsg("Songs compare finished.", 1)
|
log("Songs compare finished.", 1)
|
||||||
|
|
||||||
return True
|
return True
|
|
@ -31,7 +31,7 @@ class PlaybackUtils():
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
self.doUtils = downloadutils.DownloadUtils()
|
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||||
|
|
||||||
self.userid = utils.window('emby_currUser')
|
self.userid = utils.window('emby_currUser')
|
||||||
self.server = utils.window('emby_server%s' % self.userid)
|
self.server = utils.window('emby_server%s' % self.userid)
|
||||||
|
@ -48,7 +48,9 @@ class PlaybackUtils():
|
||||||
|
|
||||||
def play(self, itemid, dbid=None):
|
def play(self, itemid, dbid=None):
|
||||||
|
|
||||||
self.logMsg("Play called.", 1)
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
|
settings = utils.settings
|
||||||
|
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
item = self.item
|
item = self.item
|
||||||
|
@ -56,6 +58,7 @@ class PlaybackUtils():
|
||||||
listitem = xbmcgui.ListItem()
|
listitem = xbmcgui.ListItem()
|
||||||
playutils = putils.PlayUtils(item)
|
playutils = putils.PlayUtils(item)
|
||||||
|
|
||||||
|
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)
|
||||||
|
@ -74,13 +77,13 @@ class PlaybackUtils():
|
||||||
sizePlaylist = playlist.size()
|
sizePlaylist = playlist.size()
|
||||||
currentPosition = startPos
|
currentPosition = startPos
|
||||||
|
|
||||||
propertiesPlayback = utils.window('emby_playbackProps') == "true"
|
propertiesPlayback = window('emby_playbackProps') == "true"
|
||||||
introsPlaylist = False
|
introsPlaylist = False
|
||||||
dummyPlaylist = False
|
dummyPlaylist = False
|
||||||
|
|
||||||
self.logMsg("Playlist start position: %s" % startPos, 1)
|
log("Playlist start position: %s" % startPos, 2)
|
||||||
self.logMsg("Playlist plugin position: %s" % currentPosition, 1)
|
log("Playlist plugin position: %s" % currentPosition, 2)
|
||||||
self.logMsg("Playlist size: %s" % sizePlaylist, 1)
|
log("Playlist size: %s" % sizePlaylist, 2)
|
||||||
|
|
||||||
############### RESUME POINT ################
|
############### RESUME POINT ################
|
||||||
|
|
||||||
|
@ -91,13 +94,13 @@ class PlaybackUtils():
|
||||||
# Otherwise we get a loop.
|
# Otherwise we get a loop.
|
||||||
if not propertiesPlayback:
|
if not propertiesPlayback:
|
||||||
|
|
||||||
utils.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
|
||||||
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
|
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
|
||||||
|
@ -108,27 +111,27 @@ class PlaybackUtils():
|
||||||
|
|
||||||
############### -- CHECK FOR INTROS ################
|
############### -- 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
|
# 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
|
url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid
|
||||||
intros = doUtils.downloadUrl(url)
|
intros = doUtils(url)
|
||||||
|
|
||||||
if intros['TotalRecordCount'] != 0:
|
if intros['TotalRecordCount'] != 0:
|
||||||
getTrailers = True
|
getTrailers = True
|
||||||
|
|
||||||
if utils.settings('askCinema') == "true":
|
if settings('askCinema') == "true":
|
||||||
resp = xbmcgui.Dialog().yesno("Emby Cinema Mode", "Play trailers?")
|
resp = xbmcgui.Dialog().yesno("Emby for Kodi", utils.language(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)
|
||||||
|
@ -144,7 +147,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, item['Type'].lower())
|
self.pl.addtoPlaylist(dbid, item['Type'].lower())
|
||||||
|
|
||||||
# Ensure that additional parts are played after the main item
|
# 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
|
# Only add to the playlist after intros have played
|
||||||
partcount = item['PartCount']
|
partcount = item['PartCount']
|
||||||
url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
|
url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
|
||||||
parts = doUtils.downloadUrl(url)
|
parts = doUtils(url)
|
||||||
for part in parts['Items']:
|
for part in parts['Items']:
|
||||||
|
|
||||||
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)
|
||||||
|
@ -175,58 +178,60 @@ 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)
|
||||||
utils.window('emby_playbackProps', clear=True)
|
window('emby_playbackProps', clear=True)
|
||||||
|
|
||||||
#self.pl.verifyPlaylist()
|
#self.pl.verifyPlaylist()
|
||||||
########## SETUP MAIN ITEM ##########
|
########## SETUP MAIN ITEM ##########
|
||||||
|
|
||||||
# For transcoding only, ask for audio/subs pref
|
# 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)
|
playurl = playutils.audioSubsPref(playurl, listitem)
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||||
|
|
||||||
listitem.setPath(playurl)
|
listitem.setPath(playurl)
|
||||||
self.setProperties(playurl, listitem)
|
self.setProperties(playurl, listitem)
|
||||||
|
|
||||||
############### PLAYBACK ################
|
############### PLAYBACK ################
|
||||||
|
|
||||||
if homeScreen and seektime and utils.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 utils.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
|
||||||
item = self.item
|
item = self.item
|
||||||
itemid = item['Id']
|
itemid = item['Id']
|
||||||
itemtype = item['Type']
|
itemtype = item['Type']
|
||||||
|
|
||||||
embyitem = "emby_%s" % playurl
|
embyitem = "emby_%s" % playurl
|
||||||
utils.window('%s.runtime' % embyitem, value=str(item.get('RunTimeTicks')))
|
window('%s.runtime' % embyitem, value=str(item.get('RunTimeTicks')))
|
||||||
utils.window('%s.type' % embyitem, value=itemtype)
|
window('%s.type' % embyitem, value=itemtype)
|
||||||
utils.window('%s.itemid' % embyitem, value=itemid)
|
window('%s.itemid' % embyitem, value=itemid)
|
||||||
|
|
||||||
if itemtype == "Episode":
|
if itemtype == "Episode":
|
||||||
utils.window('%s.refreshid' % embyitem, value=item.get('SeriesId'))
|
window('%s.refreshid' % embyitem, value=item.get('SeriesId'))
|
||||||
else:
|
else:
|
||||||
utils.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 = utils.window('%s.playmethod' % embyitem)
|
||||||
|
@ -316,7 +321,7 @@ class PlaybackUtils():
|
||||||
def setListItem(self, listItem):
|
def setListItem(self, listItem):
|
||||||
|
|
||||||
item = self.item
|
item = self.item
|
||||||
type = item['Type']
|
itemtype = item['Type']
|
||||||
API = self.API
|
API = self.API
|
||||||
people = API.getPeople()
|
people = API.getPeople()
|
||||||
studios = API.getStudios()
|
studios = API.getStudios()
|
||||||
|
@ -336,7 +341,7 @@ class PlaybackUtils():
|
||||||
'votes': item.get('VoteCount')
|
'votes': item.get('VoteCount')
|
||||||
}
|
}
|
||||||
|
|
||||||
if "Episode" in type:
|
if "Episode" in itemtype:
|
||||||
# Only for tv shows
|
# Only for tv shows
|
||||||
thumbId = item.get('SeriesId')
|
thumbId = item.get('SeriesId')
|
||||||
season = item.get('ParentIndexNumber', -1)
|
season = item.get('ParentIndexNumber', -1)
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
self.addonName = self.clientInfo.getAddonName()
|
self.addonName = self.clientInfo.getAddonName()
|
||||||
self.doUtils = downloadutils.DownloadUtils()
|
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||||
self.ws = wsc.WebSocket_Client()
|
self.ws = wsc.WebSocket_Client()
|
||||||
self.xbmcplayer = xbmc.Player()
|
self.xbmcplayer = xbmc.Player()
|
||||||
|
|
||||||
|
@ -47,7 +47,10 @@ class Player(xbmc.Player):
|
||||||
def GetPlayStats(self):
|
def GetPlayStats(self):
|
||||||
return self.playStats
|
return self.playStats
|
||||||
|
|
||||||
def onPlayBackStarted( self ):
|
def onPlayBackStarted(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
# Will be called when xbmc starts playing a file
|
# Will be called when xbmc starts playing a file
|
||||||
xbmcplayer = self.xbmcplayer
|
xbmcplayer = self.xbmcplayer
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
@ -66,7 +69,7 @@ class Player(xbmc.Player):
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
if count == 5: # try 5 times
|
if count == 5: # try 5 times
|
||||||
self.logMsg("Cancelling playback report...", 1)
|
log("Cancelling playback report...", 1)
|
||||||
break
|
break
|
||||||
else: count += 1
|
else: count += 1
|
||||||
|
|
||||||
|
@ -76,36 +79,34 @@ class Player(xbmc.Player):
|
||||||
self.currentFile = currentFile
|
self.currentFile = currentFile
|
||||||
|
|
||||||
# We may need to wait for info to be set in kodi monitor
|
# 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
|
tryCount = 0
|
||||||
while not itemId:
|
while not itemId:
|
||||||
|
|
||||||
xbmc.sleep(200)
|
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
|
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
|
break
|
||||||
else: tryCount += 1
|
else: tryCount += 1
|
||||||
|
|
||||||
else:
|
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.
|
# Only proceed if an itemId was found.
|
||||||
embyitem = "emby_%s" % currentFile
|
embyitem = "emby_%s" % currentFile
|
||||||
runtime = utils.window("%s.runtime" % embyitem)
|
runtime = window("%s.runtime" % embyitem)
|
||||||
refresh_id = utils.window("%s.refreshid" % embyitem)
|
refresh_id = window("%s.refreshid" % embyitem)
|
||||||
playMethod = utils.window("%s.playmethod" % embyitem)
|
playMethod = window("%s.playmethod" % embyitem)
|
||||||
itemType = utils.window("%s.type" % embyitem)
|
itemType = window("%s.type" % embyitem)
|
||||||
utils.window('emby_skipWatched%s' % itemId, value="true")
|
window('emby_skipWatched%s' % itemId, value="true")
|
||||||
|
|
||||||
|
customseek = window('emby_customPlaylist.seektime')
|
||||||
if (utils.window('emby_customPlaylist') == "true" and
|
if window('emby_customPlaylist') == "true" and customseek:
|
||||||
utils.window('emby_customPlaylist.seektime')):
|
|
||||||
# Start at, when using custom playlist (play to Kodi from webclient)
|
# Start at, when using custom playlist (play to Kodi from webclient)
|
||||||
seektime = utils.window('emby_customPlaylist.seektime')
|
log("Seeking to: %s" % customseek, 1)
|
||||||
self.logMsg("Seeking to: %s" % seektime, 1)
|
xbmcplayer.seekTime(int(customseek)/10000000.0)
|
||||||
xbmcplayer.seekTime(int(seektime)/10000000.0)
|
window('emby_customPlaylist.seektime', clear=True)
|
||||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
|
||||||
|
|
||||||
seekTime = xbmcplayer.getTime()
|
seekTime = xbmcplayer.getTime()
|
||||||
|
|
||||||
|
@ -144,9 +145,8 @@ class Player(xbmc.Player):
|
||||||
# Get the current audio track and subtitles
|
# Get the current audio track and subtitles
|
||||||
if playMethod == "Transcode":
|
if playMethod == "Transcode":
|
||||||
# property set in PlayUtils.py
|
# property set in PlayUtils.py
|
||||||
postdata['AudioStreamIndex'] = utils.window("%sAudioStreamIndex" % currentFile)
|
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
|
||||||
postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex"
|
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
|
||||||
% currentFile)
|
|
||||||
else:
|
else:
|
||||||
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
||||||
tracks_query = {
|
tracks_query = {
|
||||||
|
@ -187,11 +187,11 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
# Number of audiotracks to help get Emby Index
|
# Number of audiotracks to help get Emby Index
|
||||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||||
mapping = utils.window("%s.indexMapping" % embyitem)
|
mapping = window("%s.indexMapping" % embyitem)
|
||||||
|
|
||||||
if mapping: # Set in playbackutils.py
|
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)
|
externalIndex = json.loads(mapping)
|
||||||
|
|
||||||
if externalIndex.get(str(indexSubs)):
|
if externalIndex.get(str(indexSubs)):
|
||||||
|
@ -209,15 +209,15 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
|
|
||||||
# Post playback to server
|
# Post playback to server
|
||||||
self.logMsg("Sending POST play started: %s." % postdata, 2)
|
log("Sending POST play started: %s." % postdata, 2)
|
||||||
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
self.doUtils(url, postBody=postdata, type="POST")
|
||||||
|
|
||||||
# Ensure we do have a runtime
|
# Ensure we do have a runtime
|
||||||
try:
|
try:
|
||||||
runtime = int(runtime)
|
runtime = int(runtime)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
runtime = xbmcplayer.getTotalTime()
|
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
|
# Save data map for updates and position calls
|
||||||
data = {
|
data = {
|
||||||
|
@ -234,7 +234,7 @@ class Player(xbmc.Player):
|
||||||
}
|
}
|
||||||
|
|
||||||
self.played_info[currentFile] = data
|
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
|
# log some playback stats
|
||||||
'''if(itemType != None):
|
'''if(itemType != None):
|
||||||
|
@ -253,7 +253,9 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
def reportPlayback(self):
|
def reportPlayback(self):
|
||||||
|
|
||||||
self.logMsg("reportPlayback Called", 2)
|
log = self.logMsg
|
||||||
|
|
||||||
|
log("reportPlayback Called", 2)
|
||||||
xbmcplayer = self.xbmcplayer
|
xbmcplayer = self.xbmcplayer
|
||||||
|
|
||||||
# Get current file
|
# Get current file
|
||||||
|
@ -281,7 +283,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
"properties": ["volume", "muted"]
|
"properties": ["volume", "muted"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
||||||
result = json.loads(result)
|
result = json.loads(result)
|
||||||
result = result.get('result')
|
result = result.get('result')
|
||||||
|
@ -352,7 +354,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
if mapping: # Set in PlaybackUtils.py
|
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)
|
externalIndex = json.loads(mapping)
|
||||||
|
|
||||||
if externalIndex.get(str(indexSubs)):
|
if externalIndex.get(str(indexSubs)):
|
||||||
|
@ -372,7 +374,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
# Report progress via websocketclient
|
# Report progress via websocketclient
|
||||||
postdata = json.dumps(postdata)
|
postdata = json.dumps(postdata)
|
||||||
self.logMsg("Report: %s" % postdata, 2)
|
log("Report: %s" % postdata, 2)
|
||||||
self.ws.sendProgressUpdate(postdata)
|
self.ws.sendProgressUpdate(postdata)
|
||||||
|
|
||||||
def onPlayBackPaused( self ):
|
def onPlayBackPaused( self ):
|
||||||
|
@ -407,12 +409,15 @@ class Player(xbmc.Player):
|
||||||
self.reportPlayback()
|
self.reportPlayback()
|
||||||
|
|
||||||
def onPlayBackStopped( self ):
|
def onPlayBackStopped( self ):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
# Will be called when user stops xbmc playing a file
|
# Will be called when user stops xbmc playing a file
|
||||||
self.logMsg("ONPLAYBACK_STOPPED", 2)
|
logMsg("ONPLAYBACK_STOPPED", 2)
|
||||||
utils.window('emby_customPlaylist', clear=True)
|
window('emby_customPlaylist', clear=True)
|
||||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
window('emby_customPlaylist.seektime', clear=True)
|
||||||
utils.window('emby_playbackProps', clear=True)
|
window('emby_playbackProps', clear=True)
|
||||||
self.logMsg("Clear playlist properties.", 1)
|
logMsg("Clear playlist properties.", 1)
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
def onPlayBackEnded( self ):
|
def onPlayBackEnded( self ):
|
||||||
|
@ -423,20 +428,24 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
def stopAll(self):
|
def stopAll(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
|
settings = utils.settings
|
||||||
|
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
|
|
||||||
if not self.played_info:
|
if not self.played_info:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.logMsg("Played_information: %s" % self.played_info, 1)
|
log("Played_information: %s" % self.played_info, 1)
|
||||||
# Process each items
|
# Process each items
|
||||||
for item in self.played_info:
|
for item in self.played_info:
|
||||||
|
|
||||||
data = self.played_info.get(item)
|
data = self.played_info.get(item)
|
||||||
if data:
|
if data:
|
||||||
|
|
||||||
self.logMsg("Item path: %s" % item, 2)
|
log("Item path: %s" % item, 2)
|
||||||
self.logMsg("Item data: %s" % data, 2)
|
log("Item data: %s" % data, 2)
|
||||||
|
|
||||||
runtime = data['runtime']
|
runtime = data['runtime']
|
||||||
currentPosition = data['currentPosition']
|
currentPosition = data['currentPosition']
|
||||||
|
@ -453,9 +462,8 @@ class Player(xbmc.Player):
|
||||||
# Runtime is 0.
|
# Runtime is 0.
|
||||||
percentComplete = 0
|
percentComplete = 0
|
||||||
|
|
||||||
markPlayedAt = float(utils.settings('markPlayed')) / 100
|
markPlayedAt = float(settings('markPlayed')) / 100
|
||||||
self.logMsg(
|
log("Percent complete: %s Mark played at: %s"
|
||||||
"Percent complete: %s Mark played at: %s"
|
|
||||||
% (percentComplete, markPlayedAt), 1)
|
% (percentComplete, markPlayedAt), 1)
|
||||||
|
|
||||||
# Prevent manually mark as watched in Kodi monitor
|
# Prevent manually mark as watched in Kodi monitor
|
||||||
|
@ -464,34 +472,32 @@ class Player(xbmc.Player):
|
||||||
self.stopPlayback(data)
|
self.stopPlayback(data)
|
||||||
# Stop transcoding
|
# Stop transcoding
|
||||||
if playMethod == "Transcode":
|
if playMethod == "Transcode":
|
||||||
self.logMsg("Transcoding for %s terminated." % itemid, 1)
|
log("Transcoding for %s terminated." % itemid, 1)
|
||||||
deviceId = self.clientInfo.getDeviceId()
|
deviceId = self.clientInfo.getDeviceId()
|
||||||
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
||||||
doUtils.downloadUrl(url, type="DELETE")
|
doUtils(url, type="DELETE")
|
||||||
|
|
||||||
# Send the delete action to the server.
|
# Send the delete action to the server.
|
||||||
offerDelete = False
|
offerDelete = False
|
||||||
|
|
||||||
if type == "Episode" and utils.settings('deleteTV') == "true":
|
if type == "Episode" and settings('deleteTV') == "true":
|
||||||
offerDelete = True
|
offerDelete = True
|
||||||
elif type == "Movie" and utils.settings('deleteMovies') == "true":
|
elif type == "Movie" and settings('deleteMovies') == "true":
|
||||||
offerDelete = True
|
offerDelete = True
|
||||||
|
|
||||||
if utils.settings('offerDelete') != "true":
|
if settings('offerDelete') != "true":
|
||||||
# Delete could be disabled, even if the subsetting is enabled.
|
# Delete could be disabled, even if the subsetting is enabled.
|
||||||
offerDelete = False
|
offerDelete = False
|
||||||
|
|
||||||
if percentComplete >= markPlayedAt and offerDelete:
|
if percentComplete >= markPlayedAt and offerDelete:
|
||||||
resp = xbmcgui.Dialog().yesno(
|
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015))
|
||||||
heading="Confirm delete",
|
|
||||||
line1="Delete file on Emby Server?")
|
|
||||||
if not resp:
|
if not resp:
|
||||||
self.logMsg("User skipped deletion.", 1)
|
log("User skipped deletion.", 1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||||
self.logMsg("Deleting request: %s" % itemid)
|
log("Deleting request: %s" % itemid, 1)
|
||||||
doUtils.downloadUrl(url, type="DELETE")
|
doUtils(url, type="DELETE")
|
||||||
|
|
||||||
self.played_info.clear()
|
self.played_info.clear()
|
||||||
|
|
||||||
|
@ -510,4 +516,4 @@ class Player(xbmc.Player):
|
||||||
'MediaSourceId': itemId,
|
'MediaSourceId': itemId,
|
||||||
'PositionTicks': positionTicks
|
'PositionTicks': positionTicks
|
||||||
}
|
}
|
||||||
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
self.doUtils(url, postBody=postdata, type="POST")
|
|
@ -39,6 +39,9 @@ class Playlist():
|
||||||
|
|
||||||
def playAll(self, itemids, startat):
|
def playAll(self, itemids, startat):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
|
|
||||||
embyconn = utils.kodiSQL('emby')
|
embyconn = utils.kodiSQL('emby')
|
||||||
embycursor = embyconn.cursor()
|
embycursor = embyconn.cursor()
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
|
@ -47,15 +50,15 @@ class Playlist():
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
playlist.clear()
|
playlist.clear()
|
||||||
|
|
||||||
self.logMsg("---*** PLAY ALL ***---", 1)
|
log("---*** PLAY ALL ***---", 1)
|
||||||
self.logMsg("Items: %s and start at: %s" % (itemids, startat))
|
log("Items: %s and start at: %s" % (itemids, startat), 1)
|
||||||
|
|
||||||
started = False
|
started = False
|
||||||
utils.window('emby_customplaylist', value="true")
|
window('emby_customplaylist', value="true")
|
||||||
|
|
||||||
if startat != 0:
|
if startat != 0:
|
||||||
# Seek to the starting position
|
# Seek to the starting position
|
||||||
utils.window('emby_customplaylist.seektime', str(startat))
|
window('emby_customplaylist.seektime', str(startat))
|
||||||
|
|
||||||
for itemid in itemids:
|
for itemid in itemids:
|
||||||
embydb_item = emby_db.getItem_byId(itemid)
|
embydb_item = emby_db.getItem_byId(itemid)
|
||||||
|
@ -64,14 +67,14 @@ class Playlist():
|
||||||
mediatype = embydb_item[4]
|
mediatype = embydb_item[4]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Item is not found in our database, add item manually
|
# 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)
|
item = self.emby.getItem(itemid)
|
||||||
self.addtoPlaylist_xbmc(playlist, item)
|
self.addtoPlaylist_xbmc(playlist, item)
|
||||||
else:
|
else:
|
||||||
# Add to playlist
|
# Add to playlist
|
||||||
self.addtoPlaylist(dbid, mediatype)
|
self.addtoPlaylist(dbid, mediatype)
|
||||||
|
|
||||||
self.logMsg("Adding %s to playlist." % itemid, 1)
|
log("Adding %s to playlist." % itemid, 1)
|
||||||
|
|
||||||
if not started:
|
if not started:
|
||||||
started = True
|
started = True
|
||||||
|
@ -82,12 +85,14 @@ class Playlist():
|
||||||
|
|
||||||
def modifyPlaylist(self, itemids):
|
def modifyPlaylist(self, itemids):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
embyconn = utils.kodiSQL('emby')
|
embyconn = utils.kodiSQL('emby')
|
||||||
embycursor = embyconn.cursor()
|
embycursor = embyconn.cursor()
|
||||||
emby_db = embydb.Embydb_Functions(embycursor)
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
|
|
||||||
self.logMsg("---*** ADD TO PLAYLIST ***---", 1)
|
log("---*** ADD TO PLAYLIST ***---", 1)
|
||||||
self.logMsg("Items: %s" % itemids, 1)
|
log("Items: %s" % itemids, 1)
|
||||||
|
|
||||||
player = xbmc.Player()
|
player = xbmc.Player()
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
@ -105,7 +110,7 @@ class Playlist():
|
||||||
# Add to playlist
|
# Add to playlist
|
||||||
self.addtoPlaylist(dbid, mediatype)
|
self.addtoPlaylist(dbid, mediatype)
|
||||||
|
|
||||||
self.logMsg("Adding %s to playlist." % itemid, 1)
|
log("Adding %s to playlist." % itemid, 1)
|
||||||
|
|
||||||
self.verifyPlaylist()
|
self.verifyPlaylist()
|
||||||
embycursor.close()
|
embycursor.close()
|
||||||
|
|
|
@ -33,43 +33,46 @@ class PlayUtils():
|
||||||
|
|
||||||
def getPlayUrl(self):
|
def getPlayUrl(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
|
|
||||||
item = self.item
|
item = self.item
|
||||||
playurl = None
|
playurl = None
|
||||||
|
|
||||||
if (item.get('Type') in ("Recording", "TvChannel") and
|
if (item.get('Type') in ("Recording", "TvChannel") and
|
||||||
item.get('MediaSources') and item['MediaSources'][0]['Protocol'] == "Http"):
|
item.get('MediaSources') and item['MediaSources'][0]['Protocol'] == "Http"):
|
||||||
# Play LiveTV or recordings
|
# 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'])
|
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":
|
elif item.get('MediaSources') and item['MediaSources'][0]['Protocol'] == "Http":
|
||||||
# Only play as http, used for channels, or online hosting of content
|
# 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()
|
playurl = self.httpPlay()
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
|
window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||||
|
|
||||||
elif self.isDirectPlay():
|
elif self.isDirectPlay():
|
||||||
|
|
||||||
self.logMsg("File is direct playing.", 1)
|
log("File is direct playing.", 1)
|
||||||
playurl = self.directPlay()
|
playurl = self.directPlay()
|
||||||
playurl = playurl.encode('utf-8')
|
playurl = playurl.encode('utf-8')
|
||||||
# Set playmethod property
|
# Set playmethod property
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
||||||
|
|
||||||
elif self.isDirectStream():
|
elif self.isDirectStream():
|
||||||
|
|
||||||
self.logMsg("File is direct streaming.", 1)
|
log("File is direct streaming.", 1)
|
||||||
playurl = self.directStream()
|
playurl = self.directStream()
|
||||||
# Set playmethod property
|
# Set playmethod property
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
|
window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||||
|
|
||||||
elif self.isTranscoding():
|
elif self.isTranscoding():
|
||||||
|
|
||||||
self.logMsg("File is transcoding.", 1)
|
log("File is transcoding.", 1)
|
||||||
playurl = self.transcoding()
|
playurl = self.transcoding()
|
||||||
# Set playmethod property
|
# Set playmethod property
|
||||||
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||||
|
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
|
@ -90,16 +93,21 @@ class PlayUtils():
|
||||||
|
|
||||||
def isDirectPlay(self):
|
def isDirectPlay(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
|
settings = utils.settings
|
||||||
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
||||||
item = self.item
|
item = self.item
|
||||||
|
|
||||||
# Requirement: Filesystem, Accessible path
|
# Requirement: Filesystem, Accessible path
|
||||||
if utils.settings('playFromStream') == "true":
|
if settings('playFromStream') == "true":
|
||||||
# User forcing to play via HTTP
|
# 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
|
return False
|
||||||
|
|
||||||
videotrack = item['MediaSources'][0]['Name']
|
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):
|
if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack):
|
||||||
# Avoid H265/HEVC depending on the resolution
|
# Avoid H265/HEVC depending on the resolution
|
||||||
|
@ -110,46 +118,45 @@ class PlayUtils():
|
||||||
'2': 720,
|
'2': 720,
|
||||||
'3': 1080
|
'3': 1080
|
||||||
}
|
}
|
||||||
self.logMsg("Resolution is: %sP, transcode for resolution: %sP+"
|
log("Resolution is: %sP, transcode for resolution: %sP+"
|
||||||
% (resolution, res[transcodeH265]), 1)
|
% (resolution, res[transcodeH265]), 1)
|
||||||
if res[transcodeH265] <= resolution:
|
if res[transcodeH265] <= resolution:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
canDirectPlay = item['MediaSources'][0]['SupportsDirectPlay']
|
canDirectPlay = item['MediaSources'][0]['SupportsDirectPlay']
|
||||||
# Make sure direct play is supported by the server
|
# Make sure direct play is supported by the server
|
||||||
if not canDirectPlay:
|
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
|
return False
|
||||||
|
|
||||||
location = item['LocationType']
|
location = item['LocationType']
|
||||||
if location == "FileSystem":
|
if location == "FileSystem":
|
||||||
# Verify the path
|
# Verify the path
|
||||||
if not self.fileExists():
|
if not self.fileExists():
|
||||||
self.logMsg("Unable to direct play.")
|
log("Unable to direct play.")
|
||||||
try:
|
try:
|
||||||
count = int(utils.settings('failCount'))
|
count = int(settings('failCount'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
count = 0
|
count = 0
|
||||||
self.logMsg("Direct play failed: %s times." % count, 1)
|
log("Direct play failed: %s times." % count, 1)
|
||||||
|
|
||||||
if count < 2:
|
if count < 2:
|
||||||
# Let the user know that direct play failed
|
# Let the user know that direct play failed
|
||||||
utils.settings('failCount', value=str(count+1))
|
settings('failCount', value=str(count+1))
|
||||||
xbmcgui.Dialog().notification(
|
dialog.notification(
|
||||||
heading="Emby server",
|
heading="Emby for Kodi",
|
||||||
message="Unable to direct play.",
|
message=lang(33011),
|
||||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||||
sound=False)
|
sound=False)
|
||||||
elif utils.settings('playFromStream') != "true":
|
elif settings('playFromStream') != "true":
|
||||||
# Permanently set direct stream as true
|
# Permanently set direct stream as true
|
||||||
utils.settings('playFromStream', value="true")
|
settings('playFromStream', value="true")
|
||||||
utils.settings('failCount', value="0")
|
settings('failCount', value="0")
|
||||||
xbmcgui.Dialog().notification(
|
dialog.notification(
|
||||||
heading="Emby server",
|
heading="Emby for Kodi",
|
||||||
message=("Direct play failed 3 times. Enabled play "
|
message=lang(33012),
|
||||||
"from HTTP in the add-on settings."),
|
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
sound=False)
|
||||||
sound=False)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -185,28 +192,32 @@ class PlayUtils():
|
||||||
|
|
||||||
def fileExists(self):
|
def fileExists(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
if 'Path' not in self.item:
|
if 'Path' not in self.item:
|
||||||
# File has no path defined in server
|
# File has no path defined in server
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Convert path to direct play
|
# Convert path to direct play
|
||||||
path = self.directPlay()
|
path = self.directPlay()
|
||||||
self.logMsg("Verifying path: %s" % path, 1)
|
log("Verifying path: %s" % path, 1)
|
||||||
|
|
||||||
if xbmcvfs.exists(path):
|
if xbmcvfs.exists(path):
|
||||||
self.logMsg("Path exists.", 1)
|
log("Path exists.", 1)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif ":" not in path:
|
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
|
return True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.logMsg("Failed to find file.")
|
log("Failed to find file.", 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def isDirectStream(self):
|
def isDirectStream(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
item = self.item
|
item = self.item
|
||||||
|
|
||||||
videotrack = item['MediaSources'][0]['Name']
|
videotrack = item['MediaSources'][0]['Name']
|
||||||
|
@ -221,8 +232,8 @@ class PlayUtils():
|
||||||
'2': 720,
|
'2': 720,
|
||||||
'3': 1080
|
'3': 1080
|
||||||
}
|
}
|
||||||
self.logMsg("Resolution is: %sP, transcode for resolution: %sP+"
|
log("Resolution is: %sP, transcode for resolution: %sP+"
|
||||||
% (resolution, res[transcodeH265]), 1)
|
% (resolution, res[transcodeH265]), 1)
|
||||||
if res[transcodeH265] <= resolution:
|
if res[transcodeH265] <= resolution:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -234,7 +245,7 @@ class PlayUtils():
|
||||||
|
|
||||||
# Verify the bitrate
|
# Verify the bitrate
|
||||||
if not self.isNetworkSufficient():
|
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 False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -245,12 +256,12 @@ class PlayUtils():
|
||||||
server = self.server
|
server = self.server
|
||||||
|
|
||||||
itemid = item['Id']
|
itemid = item['Id']
|
||||||
type = item['Type']
|
itemtype = item['Type']
|
||||||
|
|
||||||
if 'Path' in item and item['Path'].endswith('.strm'):
|
if 'Path' in item and item['Path'].endswith('.strm'):
|
||||||
# Allow strm loading when direct streaming
|
# Allow strm loading when direct streaming
|
||||||
playurl = self.directPlay()
|
playurl = self.directPlay()
|
||||||
elif type == "Audio":
|
elif itemtype == "Audio":
|
||||||
playurl = "%s/emby/Audio/%s/stream.mp3" % (server, itemid)
|
playurl = "%s/emby/Audio/%s/stream.mp3" % (server, itemid)
|
||||||
else:
|
else:
|
||||||
playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
|
playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
|
||||||
|
@ -259,15 +270,17 @@ class PlayUtils():
|
||||||
|
|
||||||
def isNetworkSufficient(self):
|
def isNetworkSufficient(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
settings = self.getBitrate()*1000
|
settings = self.getBitrate()*1000
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
|
sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
self.logMsg("Bitrate value is missing.", 1)
|
log("Bitrate value is missing.", 1)
|
||||||
else:
|
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)
|
% (settings, sourceBitrate), 1)
|
||||||
if settings < sourceBitrate:
|
if settings < sourceBitrate:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -335,6 +348,10 @@ class PlayUtils():
|
||||||
return bitrate.get(videoQuality, 2147483)
|
return bitrate.get(videoQuality, 2147483)
|
||||||
|
|
||||||
def audioSubsPref(self, url, listitem):
|
def audioSubsPref(self, url, listitem):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
|
dialog = xbmcgui.Dialog()
|
||||||
# For transcoding only
|
# For transcoding only
|
||||||
# Present the list of audio to select from
|
# Present the list of audio to select from
|
||||||
audioStreamsList = {}
|
audioStreamsList = {}
|
||||||
|
@ -373,8 +390,6 @@ class PlayUtils():
|
||||||
audioStreams.append(track)
|
audioStreams.append(track)
|
||||||
|
|
||||||
elif 'Subtitle' in type:
|
elif 'Subtitle' in type:
|
||||||
'''if stream['IsExternal']:
|
|
||||||
continue'''
|
|
||||||
try:
|
try:
|
||||||
track = "%s - %s" % (index, stream['Language'])
|
track = "%s - %s" % (index, stream['Language'])
|
||||||
except:
|
except:
|
||||||
|
@ -396,7 +411,7 @@ class PlayUtils():
|
||||||
|
|
||||||
|
|
||||||
if len(audioStreams) > 1:
|
if len(audioStreams) > 1:
|
||||||
resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
|
resp = dialog.select(lang(33013), audioStreams)
|
||||||
if resp > -1:
|
if resp > -1:
|
||||||
# User selected audio
|
# User selected audio
|
||||||
selected = audioStreams[resp]
|
selected = audioStreams[resp]
|
||||||
|
@ -409,7 +424,7 @@ class PlayUtils():
|
||||||
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
|
||||||
|
|
||||||
if len(subtitleStreams) > 1:
|
if len(subtitleStreams) > 1:
|
||||||
resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
|
resp = dialog.select(lang(33014), subtitleStreams)
|
||||||
if resp == 0:
|
if resp == 0:
|
||||||
# User selected no subtitles
|
# User selected no subtitles
|
||||||
pass
|
pass
|
||||||
|
@ -424,7 +439,7 @@ class PlayUtils():
|
||||||
itemid = item['Id']
|
itemid = item['Id']
|
||||||
url = [("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
|
url = [("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
|
||||||
% (self.server, itemid, itemid, selectSubsIndex))]
|
% (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)
|
listitem.setSubtitles(url)
|
||||||
else:
|
else:
|
||||||
# Burn subtitles
|
# Burn subtitles
|
||||||
|
|
|
@ -18,12 +18,14 @@ class Read_EmbyServer():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
window = utils.window
|
||||||
|
|
||||||
self.clientInfo = clientinfo.ClientInfo()
|
self.clientInfo = clientinfo.ClientInfo()
|
||||||
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)
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
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):
|
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
items = {
|
items = {
|
||||||
|
|
||||||
|
@ -208,7 +212,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
|
||||||
|
@ -250,27 +254,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, 1)
|
log("Set jump limit to recover: %s" % jump, 2)
|
||||||
|
|
||||||
retry = 0
|
retry = 0
|
||||||
while utils.window('emby_online') != "true":
|
while utils.window('emby_online') != "true":
|
||||||
# Wait server to come back online
|
# Wait server to come back online
|
||||||
if retry == 3:
|
if retry == 3:
|
||||||
self.logMsg("Unable to reconnect to server. Abort process.", 1)
|
log("Unable to reconnect to server. Abort process.", 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
retry += 1
|
retry += 1
|
||||||
|
@ -298,7 +302,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, type, root=False):
|
def getViews(self, type, root=False):
|
||||||
|
|
|
@ -81,42 +81,46 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def getUserId(self):
|
def getUserId(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
|
settings = utils.settings
|
||||||
|
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
w_userId = utils.window('emby_userId%s' % username)
|
w_userId = window('emby_currUser')
|
||||||
s_userId = utils.settings('userId%s' % username)
|
s_userId = settings('userId%s' % username)
|
||||||
|
|
||||||
# Verify the window property
|
# Verify the window property
|
||||||
if w_userId:
|
if w_userId:
|
||||||
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
|
||||||
utils.settings('userId%s' % username, value=w_userId)
|
settings('userId%s' % username, value=w_userId)
|
||||||
self.logMsg(
|
log("Returning userId from WINDOW for username: %s UserId: %s"
|
||||||
"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(
|
log("Returning userId from SETTINGS for username: %s userId: %s"
|
||||||
"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):
|
||||||
|
|
||||||
alternate = utils.settings('altip') == "true"
|
settings = utils.settings
|
||||||
|
|
||||||
|
alternate = settings('altip') == "true"
|
||||||
if alternate:
|
if alternate:
|
||||||
# Alternate host
|
# Alternate host
|
||||||
HTTPS = utils.settings('secondhttps') == "true"
|
HTTPS = settings('secondhttps') == "true"
|
||||||
host = utils.settings('secondipaddress')
|
host = settings('secondipaddress')
|
||||||
port = utils.settings('secondport')
|
port = settings('secondport')
|
||||||
else:
|
else:
|
||||||
# Original host
|
# Original host
|
||||||
HTTPS = utils.settings('https') == "true"
|
HTTPS = settings('https') == "true"
|
||||||
host = utils.settings('ipaddress')
|
host = settings('ipaddress')
|
||||||
port = utils.settings('port')
|
port = settings('port')
|
||||||
|
|
||||||
server = host + ":" + port
|
server = host + ":" + port
|
||||||
|
|
||||||
|
@ -138,35 +142,40 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def getToken(self):
|
def getToken(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
|
settings = utils.settings
|
||||||
|
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
w_token = utils.window('emby_accessToken%s' % username)
|
userId = self.getUserId()
|
||||||
s_token = utils.settings('accessToken')
|
w_token = window('emby_accessToken%s' % userId)
|
||||||
|
s_token = settings('accessToken')
|
||||||
|
|
||||||
# Verify the window property
|
# Verify the window property
|
||||||
if w_token:
|
if w_token:
|
||||||
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
|
||||||
utils.settings('accessToken', value=w_token)
|
settings('accessToken', value=w_token)
|
||||||
self.logMsg(
|
log("Returning accessToken from WINDOW for username: %s accessToken: %s"
|
||||||
"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(
|
log("Returning accessToken from SETTINGS for username: %s accessToken: %s"
|
||||||
"Returning accessToken from SETTINGS for username: %s accessToken: %s"
|
|
||||||
% (username, s_token), 2)
|
% (username, s_token), 2)
|
||||||
utils.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
|
||||||
s_sslverify = utils.settings('sslverify')
|
settings = utils.settings
|
||||||
if utils.settings('altip') == "true":
|
|
||||||
s_sslverify = utils.settings('secondsslverify')
|
s_sslverify = settings('sslverify')
|
||||||
|
if settings('altip') == "true":
|
||||||
|
s_sslverify = settings('secondsslverify')
|
||||||
|
|
||||||
if s_sslverify == "true":
|
if s_sslverify == "true":
|
||||||
return True
|
return True
|
||||||
|
@ -175,9 +184,11 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def getSSL(self):
|
def getSSL(self):
|
||||||
# Client side certificate
|
# Client side certificate
|
||||||
s_cert = utils.settings('sslcert')
|
settings = utils.settings
|
||||||
if utils.settings('altip') == "true":
|
|
||||||
s_cert = utils.settings('secondsslcert')
|
s_cert = settings('sslcert')
|
||||||
|
if settings('altip') == "true":
|
||||||
|
s_cert = settings('secondsslcert')
|
||||||
|
|
||||||
if s_cert == "None":
|
if s_cert == "None":
|
||||||
return None
|
return None
|
||||||
|
@ -186,11 +197,11 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def setUserPref(self):
|
def setUserPref(self):
|
||||||
|
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils.downloadUrl
|
||||||
art = artwork.Artwork()
|
art = artwork.Artwork()
|
||||||
|
|
||||||
url = "{server}/emby/Users/{UserId}?format=json"
|
url = "{server}/emby/Users/{UserId}?format=json"
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils(url)
|
||||||
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'):
|
||||||
|
@ -198,7 +209,7 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
# Set resume point max
|
# Set resume point max
|
||||||
url = "{server}/emby/System/Configuration?format=json"
|
url = "{server}/emby/System/Configuration?format=json"
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils(url)
|
||||||
|
|
||||||
utils.settings('markPlayed', value=str(result['MaxResumePct']))
|
utils.settings('markPlayed', value=str(result['MaxResumePct']))
|
||||||
|
|
||||||
|
@ -218,26 +229,31 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def hasAccess(self):
|
def hasAccess(self):
|
||||||
# hasAccess is verified in service.py
|
# hasAccess is verified in service.py
|
||||||
|
log = self.logMsg
|
||||||
|
window = utils.window
|
||||||
|
|
||||||
url = "{server}/emby/Users?format=json"
|
url = "{server}/emby/Users?format=json"
|
||||||
result = self.doUtils.downloadUrl(url)
|
result = self.doUtils.downloadUrl(url)
|
||||||
|
|
||||||
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 utils.window('emby_online') != "true":
|
elif window('emby_online') != "true":
|
||||||
# Server connection failed
|
# Server connection failed
|
||||||
pass
|
pass
|
||||||
|
|
||||||
elif utils.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
|
||||||
utils.window('emby_serverStatus', clear=True)
|
window('emby_serverStatus', clear=True)
|
||||||
xbmcgui.Dialog().notification("Emby server", "Access is enabled.")
|
xbmcgui.Dialog().notification("Emby for Kodi", utils.language(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()
|
||||||
|
@ -252,8 +268,8 @@ class UserClient(threading.Thread):
|
||||||
# Test the validity of current token
|
# Test the validity of current token
|
||||||
if authenticated == False:
|
if authenticated == False:
|
||||||
url = "%s/emby/Users/%s?format=json" % (self.currServer, userId)
|
url = "%s/emby/Users/%s?format=json" % (self.currServer, userId)
|
||||||
utils.window('emby_currUser', value=userId)
|
window('emby_currUser', value=userId)
|
||||||
utils.window('emby_accessToken%s' % userId, value=self.currToken)
|
window('emby_accessToken%s' % userId, value=self.currToken)
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils.downloadUrl(url)
|
||||||
|
|
||||||
if result == 401:
|
if result == 401:
|
||||||
|
@ -262,10 +278,10 @@ class UserClient(threading.Thread):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Set to windows property
|
# Set to windows property
|
||||||
utils.window('emby_currUser', value=userId)
|
window('emby_currUser', value=userId)
|
||||||
utils.window('emby_accessToken%s' % userId, value=self.currToken)
|
window('emby_accessToken%s' % userId, value=self.currToken)
|
||||||
utils.window('emby_server%s' % userId, value=self.currServer)
|
window('emby_server%s' % userId, value=self.currServer)
|
||||||
utils.window('emby_server_%s' % userId, value=self.getServer(prefix=False))
|
window('emby_server_%s' % userId, value=self.getServer(prefix=False))
|
||||||
|
|
||||||
# Set DownloadUtils values
|
# Set DownloadUtils values
|
||||||
doUtils.setUsername(username)
|
doUtils.setUsername(username)
|
||||||
|
@ -284,6 +300,13 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
|
|
||||||
def authenticate(self):
|
def authenticate(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
lang = utils.language
|
||||||
|
window = utils.window
|
||||||
|
settings = utils.settings
|
||||||
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
||||||
# Get /profile/addon_data
|
# Get /profile/addon_data
|
||||||
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
|
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
|
||||||
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
|
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
|
||||||
|
@ -293,12 +316,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
|
||||||
|
@ -308,9 +331,9 @@ class UserClient(threading.Thread):
|
||||||
if result == False:
|
if result == 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 #####
|
||||||
|
@ -325,92 +348,93 @@ class UserClient(threading.Thread):
|
||||||
if username.decode('utf-8') in name:
|
if username.decode('utf-8') in name:
|
||||||
# If user has password
|
# If user has password
|
||||||
if user['HasPassword'] == True:
|
if user['HasPassword'] == True:
|
||||||
password = xbmcgui.Dialog().input(
|
password = dialog.input(
|
||||||
heading="Enter password for user: %s" % username,
|
heading="%s %s" % (lang(33008), username),
|
||||||
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)
|
||||||
utils.window('emby_serverStatus', value="Stop")
|
window('emby_serverStatus', value="Stop")
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# Manual login, user is hidden
|
# Manual login, user is hidden
|
||||||
password = xbmcgui.Dialog().input(
|
password = dialog.input(
|
||||||
heading="Enter password for user: %s" % username,
|
heading="%s %s" % (lang(33008), username),
|
||||||
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
||||||
sha1 = hashlib.sha1(password)
|
sha1 = hashlib.sha1(password)
|
||||||
sha1 = sha1.hexdigest()
|
sha1 = sha1.hexdigest()
|
||||||
|
|
||||||
# Authenticate username and password
|
# Authenticate username and password
|
||||||
url = "%s/emby/Users/AuthenticateByName?format=json" % server
|
url = "%s/emby/Users/AuthenticateByName?format=json" % server
|
||||||
data = {'username': username, 'password': sha1}
|
data = {'username': username, 'password': sha1}
|
||||||
self.logMsg(data, 2)
|
log(data, 2)
|
||||||
|
|
||||||
result = self.doUtils.downloadUrl(url, postBody=data, type="POST", authenticate=False)
|
result = self.doUtils.downloadUrl(url, postBody=data, 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:
|
||||||
self.currUser = username
|
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']
|
userId = result['User']['Id']
|
||||||
utils.settings('accessToken', value=accessToken)
|
settings('accessToken', value=accessToken)
|
||||||
utils.settings('userId%s' % username, value=userId)
|
settings('userId%s' % username, value=userId)
|
||||||
self.logMsg("User Authenticated: %s" % accessToken, 1)
|
log("User Authenticated: %s" % accessToken, 1)
|
||||||
self.loadCurrUser(authenticated=True)
|
self.loadCurrUser(authenticated=True)
|
||||||
utils.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)
|
||||||
utils.settings('accessToken', value="")
|
settings('accessToken', value="")
|
||||||
utils.settings('userId%s' % username, value="")
|
settings('userId%s' % username, value="")
|
||||||
xbmcgui.Dialog().ok("Error connecting", "Invalid username or password.")
|
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(
|
log("Too many retries. "
|
||||||
"""Too many retries. You can retry by resetting
|
"You can retry by resetting attempts in the addon settings.", 1)
|
||||||
attempts in the addon settings.""", 1)
|
window('emby_serverStatus', value="Stop")
|
||||||
utils.window('emby_serverStatus', value="Stop")
|
dialog.ok(lang(33001), lang(33010))
|
||||||
xbmcgui.Dialog().ok(
|
|
||||||
heading="Error connecting",
|
|
||||||
line1="Failed to authenticate too many times.",
|
|
||||||
line2="You can retry by resetting attempts in the addon settings.")
|
|
||||||
|
|
||||||
self.retry += 1
|
self.retry += 1
|
||||||
self.auth = False
|
self.auth = False
|
||||||
|
|
||||||
def resetClient(self):
|
def resetClient(self):
|
||||||
|
|
||||||
self.logMsg("Reset UserClient authentication.", 1)
|
log = self.logMsg
|
||||||
username = self.getUsername()
|
|
||||||
|
log("Reset UserClient authentication.", 1)
|
||||||
|
userId = self.getUserId()
|
||||||
|
|
||||||
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="")
|
utils.settings('accessToken', value="")
|
||||||
utils.window('emby_accessToken%s' % username, clear=True)
|
utils.window('emby_accessToken%s' % userId, 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):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
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():
|
||||||
|
|
||||||
status = utils.window('emby_serverStatus')
|
status = window('emby_serverStatus')
|
||||||
if status:
|
if status:
|
||||||
# Verify the connection status to server
|
# Verify the connection status to server
|
||||||
if status == "restricted":
|
if status == "restricted":
|
||||||
|
@ -419,12 +443,12 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
elif status == "401":
|
elif status == "401":
|
||||||
# Unauthorized access, revoke token
|
# Unauthorized access, revoke token
|
||||||
utils.window('emby_serverStatus', value="Auth")
|
window('emby_serverStatus', value="Auth")
|
||||||
self.resetClient()
|
self.resetClient()
|
||||||
|
|
||||||
if self.auth and (self.currUser is None):
|
if self.auth and (self.currUser is None):
|
||||||
# Try to authenticate user
|
# Try to authenticate user
|
||||||
status = utils.window('emby_serverStatus')
|
status = window('emby_serverStatus')
|
||||||
if not status or status == "Auth":
|
if not status or status == "Auth":
|
||||||
# Set auth flag because we no longer need
|
# Set auth flag because we no longer need
|
||||||
# to authenticate the user
|
# to authenticate the user
|
||||||
|
@ -436,13 +460,13 @@ class UserClient(threading.Thread):
|
||||||
# If authenticate failed.
|
# If authenticate failed.
|
||||||
server = self.getServer()
|
server = self.getServer()
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
status = utils.window('emby_serverStatus')
|
status = window('emby_serverStatus')
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
|
|
||||||
|
@ -455,7 +479,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
|
||||||
|
|
|
@ -23,7 +23,7 @@ class VideoNodes(object):
|
||||||
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):
|
def logMsg(self, msg, lvl=1):
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class VideoNodes(object):
|
||||||
xbmcvfs.exists(path)
|
xbmcvfs.exists(path)
|
||||||
|
|
||||||
# Create the node directory
|
# Create the node directory
|
||||||
if not xbmcvfs.exists(nodepath) and not mediatype=="photos":
|
if not xbmcvfs.exists(nodepath) and not mediatype == "photos":
|
||||||
# We need to copy over the default items
|
# We need to copy over the default items
|
||||||
xbmcvfs.mkdirs(nodepath)
|
xbmcvfs.mkdirs(nodepath)
|
||||||
else:
|
else:
|
||||||
|
@ -102,7 +102,7 @@ class VideoNodes(object):
|
||||||
window('Emby.nodes.%s.index' % indexnumber, value=path)
|
window('Emby.nodes.%s.index' % indexnumber, value=path)
|
||||||
|
|
||||||
# Root
|
# Root
|
||||||
if not mediatype=="photos":
|
if not mediatype == "photos":
|
||||||
root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0)
|
root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0)
|
||||||
try:
|
try:
|
||||||
utils.indent(root)
|
utils.indent(root)
|
||||||
|
@ -181,7 +181,7 @@ class VideoNodes(object):
|
||||||
nodeXML = "%s%s_%s.xml" % (nodepath, cleantagname, nodetype)
|
nodeXML = "%s%s_%s.xml" % (nodepath, cleantagname, nodetype)
|
||||||
# Get label
|
# Get label
|
||||||
stringid = nodes[node]
|
stringid = nodes[node]
|
||||||
if node != '1':
|
if node != "1":
|
||||||
label = utils.language(stringid)
|
label = utils.language(stringid)
|
||||||
if not label:
|
if not label:
|
||||||
label = xbmc.getLocalizedString(stringid)
|
label = xbmc.getLocalizedString(stringid)
|
||||||
|
@ -323,7 +323,7 @@ class VideoNodes(object):
|
||||||
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')
|
||||||
nodeXML = "%semby_%s.xml" % (nodepath, cleantagname)
|
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
|
windowpath = "ActivateWindow(Video,%s,return)" % path
|
||||||
|
|
||||||
# Create the video node directory
|
# Create the video node directory
|
||||||
|
|
|
@ -324,4 +324,4 @@ class WebSocket_Client(threading.Thread):
|
||||||
|
|
||||||
self.stopWebsocket = True
|
self.stopWebsocket = True
|
||||||
self.client.close()
|
self.client.close()
|
||||||
self.logMsg("Stopping thread.")
|
self.logMsg("Stopping thread.", 1)
|
|
@ -277,15 +277,15 @@ class Service():
|
||||||
|
|
||||||
##### Emby thread is terminating. #####
|
##### Emby thread is terminating. #####
|
||||||
|
|
||||||
|
if self.userclient_running:
|
||||||
|
user.stopClient()
|
||||||
|
|
||||||
if self.library_running:
|
if self.library_running:
|
||||||
library.stopThread()
|
library.stopThread()
|
||||||
|
|
||||||
if self.websocket_running:
|
if self.websocket_running:
|
||||||
ws.stopClient()
|
ws.stopClient()
|
||||||
|
|
||||||
if self.userclient_running:
|
|
||||||
user.stopClient()
|
|
||||||
|
|
||||||
log("======== STOP %s ========" % self.addonName, 0)
|
log("======== STOP %s ========" % self.addonName, 0)
|
||||||
|
|
||||||
# Delay option
|
# Delay option
|
||||||
|
|
Loading…
Reference in a new issue