Merge remote-tracking branch 'MediaBrowser/master' into develop
This commit is contained in:
commit
9d50e8ef30
6 changed files with 166 additions and 42 deletions
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.plexkodiconnect"
|
<addon id="plugin.video.plexkodiconnect"
|
||||||
name="PlexKodiConnect"
|
name="PlexKodiConnect"
|
||||||
version="2.0.4"
|
version="2.1.0"
|
||||||
provider-name="croneter">
|
provider-name="croneter">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.1.0"/>
|
<import addon="xbmc.python" version="2.1.0"/>
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
version 2.1.0
|
||||||
|
- Add a throttle (automatically adjust the number of items requested at once) to prevent crashing during the initial sync
|
||||||
|
- Do not update the video library when there's a music-only update
|
||||||
|
|
||||||
version 2.0.3
|
version 2.0.3
|
||||||
- Add new retention time option that the latest server Sync plugin uses to help determine if full sync or inc sync should be used.
|
- Add new retention time option that the latest server Sync plugin uses to help determine if full sync or inc sync should be used.
|
||||||
- Add control over new content pop up display time. You will find the settings under Extras > Enable new content notification
|
- Add control over new content pop up display time. You will find the settings under Extras > Enable new content notification
|
||||||
|
|
|
@ -100,6 +100,14 @@ class Main:
|
||||||
if mode == "settings":
|
if mode == "settings":
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
|
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
|
||||||
elif mode in ("manualsync", "repair"):
|
elif mode in ("manualsync", "repair"):
|
||||||
|
if utils.window('emby_online') != "true":
|
||||||
|
# Server is not online, do not run the sync
|
||||||
|
xbmcgui.Dialog().ok(heading="Emby for Kodi",
|
||||||
|
line1=("Unable to run the sync, the add-on is not "
|
||||||
|
"connected to the Emby server."))
|
||||||
|
utils.logMsg("EMBY", "Not connected to the emby server.", 1)
|
||||||
|
return
|
||||||
|
|
||||||
if utils.window('emby_dbScan') != "true":
|
if utils.window('emby_dbScan') != "true":
|
||||||
import librarysync
|
import librarysync
|
||||||
lib = librarysync.LibrarySync()
|
lib = librarysync.LibrarySync()
|
||||||
|
|
|
@ -85,6 +85,7 @@ class Items(object):
|
||||||
'Audio': Music
|
'Audio': Music
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_videolibrary = False
|
||||||
total = 0
|
total = 0
|
||||||
for item in items:
|
for item in items:
|
||||||
total += len(items[item])
|
total += len(items[item])
|
||||||
|
@ -120,6 +121,7 @@ class Items(object):
|
||||||
# Music is not enabled, do not proceed with itemtype
|
# Music is not enabled, do not proceed with itemtype
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
update_videolibrary = True
|
||||||
items_process = itemtypes[itemtype](embycursor, kodicursor)
|
items_process = itemtypes[itemtype](embycursor, kodicursor)
|
||||||
|
|
||||||
if itemtype == "Movie":
|
if itemtype == "Movie":
|
||||||
|
@ -214,10 +216,11 @@ class Items(object):
|
||||||
|
|
||||||
if musicconn is not None:
|
if musicconn is not None:
|
||||||
# close connection for special types
|
# close connection for special types
|
||||||
|
self.logMsg("Updating music database.", 1)
|
||||||
musicconn.commit()
|
musicconn.commit()
|
||||||
musiccursor.close()
|
musiccursor.close()
|
||||||
|
|
||||||
return True
|
return (True, update_videolibrary)
|
||||||
|
|
||||||
def contentPop(self, name, time=5000):
|
def contentPop(self, name, time=5000):
|
||||||
xbmcgui.Dialog().notification(
|
xbmcgui.Dialog().notification(
|
||||||
|
|
|
@ -1016,6 +1016,117 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Reserved for websocket_client.py and fast start
|
||||||
|
def triage_items(self, process, items):
|
||||||
|
|
||||||
|
processlist = {
|
||||||
|
|
||||||
|
'added': self.addedItems,
|
||||||
|
'update': self.updateItems,
|
||||||
|
'userdata': self.userdataItems,
|
||||||
|
'remove': self.removeItems
|
||||||
|
}
|
||||||
|
if items:
|
||||||
|
if process == "userdata":
|
||||||
|
itemids = []
|
||||||
|
for item in items:
|
||||||
|
itemids.append(item['ItemId'])
|
||||||
|
items = itemids
|
||||||
|
|
||||||
|
self.logMsg("Queue %s: %s" % (process, items), 1)
|
||||||
|
processlist[process].extend(items)
|
||||||
|
|
||||||
|
def incrementalSync(self):
|
||||||
|
|
||||||
|
embyconn = utils.kodiSQL('emby')
|
||||||
|
embycursor = embyconn.cursor()
|
||||||
|
kodiconn = utils.kodiSQL('video')
|
||||||
|
kodicursor = kodiconn.cursor()
|
||||||
|
emby = self.emby
|
||||||
|
emby_db = embydb.Embydb_Functions(embycursor)
|
||||||
|
pDialog = None
|
||||||
|
update_embydb = False
|
||||||
|
|
||||||
|
if self.refresh_views:
|
||||||
|
# Received userconfig update
|
||||||
|
self.refresh_views = False
|
||||||
|
self.maintainViews(embycursor, kodicursor)
|
||||||
|
self.forceLibraryUpdate = True
|
||||||
|
update_embydb = True
|
||||||
|
|
||||||
|
if self.addedItems or self.updateItems or self.userdataItems or self.removeItems:
|
||||||
|
# Only present dialog if we are going to process items
|
||||||
|
pDialog = self.progressDialog('Incremental sync')
|
||||||
|
|
||||||
|
|
||||||
|
process = {
|
||||||
|
|
||||||
|
'added': self.addedItems,
|
||||||
|
'update': self.updateItems,
|
||||||
|
'userdata': self.userdataItems,
|
||||||
|
'remove': self.removeItems
|
||||||
|
}
|
||||||
|
types = ['added', 'update', 'userdata', 'remove']
|
||||||
|
for type in types:
|
||||||
|
|
||||||
|
if process[type] and utils.window('emby_kodiScan') != "true":
|
||||||
|
|
||||||
|
listItems = list(process[type])
|
||||||
|
del process[type][:] # Reset class list
|
||||||
|
|
||||||
|
items_process = itemtypes.Items(embycursor, kodicursor)
|
||||||
|
update = False
|
||||||
|
|
||||||
|
# Prepare items according to process type
|
||||||
|
if type == "added":
|
||||||
|
items = emby.sortby_mediatype(listItems)
|
||||||
|
|
||||||
|
elif type in ("userdata", "remove"):
|
||||||
|
items = emby_db.sortby_mediaType(listItems, unsorted=False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
items = emby_db.sortby_mediaType(listItems)
|
||||||
|
if items.get('Unsorted'):
|
||||||
|
sorted_items = emby.sortby_mediatype(items['Unsorted'])
|
||||||
|
doupdate = items_process.itemsbyId(sorted_items, "added", pDialog)
|
||||||
|
if doupdate:
|
||||||
|
embyupdate, kodiupdate_video = doupdate
|
||||||
|
if embyupdate:
|
||||||
|
update_embydb = True
|
||||||
|
if kodiupdate_video:
|
||||||
|
self.forceLibraryUpdate = True
|
||||||
|
del items['Unsorted']
|
||||||
|
|
||||||
|
doupdate = items_process.itemsbyId(items, type, pDialog)
|
||||||
|
if doupdate:
|
||||||
|
embyupdate, kodiupdate_video = doupdate
|
||||||
|
if embyupdate:
|
||||||
|
update_embydb = True
|
||||||
|
if kodiupdate_video:
|
||||||
|
self.forceLibraryUpdate = True
|
||||||
|
|
||||||
|
if update_embydb:
|
||||||
|
update_embydb = False
|
||||||
|
self.logMsg("Updating emby database.", 1)
|
||||||
|
embyconn.commit()
|
||||||
|
self.saveLastSync()
|
||||||
|
|
||||||
|
if self.forceLibraryUpdate:
|
||||||
|
# Force update the Kodi library
|
||||||
|
self.forceLibraryUpdate = False
|
||||||
|
self.dbCommit(kodiconn)
|
||||||
|
|
||||||
|
self.logMsg("Updating video library.", 1)
|
||||||
|
utils.window('emby_kodiScan', value="true")
|
||||||
|
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||||
|
|
||||||
|
if pDialog:
|
||||||
|
pDialog.close()
|
||||||
|
|
||||||
|
kodicursor.close()
|
||||||
|
embycursor.close()
|
||||||
|
|
||||||
|
|
||||||
def compareDBVersion(self, current, minimum):
|
def compareDBVersion(self, current, minimum):
|
||||||
# It returns True is database is up to date. False otherwise.
|
# It returns True is database is up to date. False otherwise.
|
||||||
self.logMsg("current: %s minimum: %s" % (current, minimum), 1)
|
self.logMsg("current: %s minimum: %s" % (current, minimum), 1)
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
import xbmc
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
import downloadutils
|
import downloadutils
|
||||||
|
|
||||||
|
@ -15,7 +17,7 @@ class Read_EmbyServer():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
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)
|
||||||
|
@ -30,7 +32,7 @@ class Read_EmbyServer():
|
||||||
item = {}
|
item = {}
|
||||||
|
|
||||||
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
|
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
|
||||||
result = self.doUtils.downloadUrl(url)
|
result = self.doUtils(url)
|
||||||
if result:
|
if result:
|
||||||
item = result
|
item = result
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ class Read_EmbyServer():
|
||||||
'Ids': ",".join(itemlist),
|
'Ids': ",".join(itemlist),
|
||||||
'Fields': "Etag"
|
'Fields': "Etag"
|
||||||
}
|
}
|
||||||
result = self.doUtils.downloadUrl(url, parameters=params)
|
result = self.doUtils(url, parameters=params)
|
||||||
if result:
|
if result:
|
||||||
items.extend(result['Items'])
|
items.extend(result['Items'])
|
||||||
|
|
||||||
|
@ -76,7 +78,7 @@ class Read_EmbyServer():
|
||||||
"MediaSources"
|
"MediaSources"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
result = self.doUtils.downloadUrl(url, parameters=params)
|
result = self.doUtils(url, parameters=params)
|
||||||
if result:
|
if result:
|
||||||
items.extend(result['Items'])
|
items.extend(result['Items'])
|
||||||
|
|
||||||
|
@ -86,7 +88,7 @@ class Read_EmbyServer():
|
||||||
# Returns ancestors using embyId
|
# Returns ancestors using embyId
|
||||||
viewId = None
|
viewId = None
|
||||||
url = "{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid
|
url = "{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid
|
||||||
result = self.doUtils.downloadUrl(url)
|
result = self.doUtils(url)
|
||||||
|
|
||||||
for view in result:
|
for view in result:
|
||||||
|
|
||||||
|
@ -138,7 +140,7 @@ class Read_EmbyServer():
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||||
}
|
}
|
||||||
return doUtils.downloadUrl(url, parameters=params)
|
return doUtils(url, parameters=params)
|
||||||
|
|
||||||
def getTvChannels(self):
|
def getTvChannels(self):
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
|
@ -152,7 +154,7 @@ class Read_EmbyServer():
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||||
}
|
}
|
||||||
return doUtils.downloadUrl(url, parameters=params)
|
return doUtils(url, parameters=params)
|
||||||
|
|
||||||
def getTvRecordings(self, groupid):
|
def getTvRecordings(self, groupid):
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
|
@ -168,7 +170,7 @@ class Read_EmbyServer():
|
||||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||||
}
|
}
|
||||||
return doUtils.downloadUrl(url, parameters=params)
|
return doUtils(url, parameters=params)
|
||||||
|
|
||||||
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
|
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
|
||||||
|
|
||||||
|
@ -191,7 +193,7 @@ class Read_EmbyServer():
|
||||||
'Recursive': True,
|
'Recursive': True,
|
||||||
'Limit': 1
|
'Limit': 1
|
||||||
}
|
}
|
||||||
result = doUtils.downloadUrl(url, parameters=params)
|
result = doUtils(url, parameters=params)
|
||||||
try:
|
try:
|
||||||
total = result['TotalRecordCount']
|
total = result['TotalRecordCount']
|
||||||
items['TotalRecordCount'] = total
|
items['TotalRecordCount'] = total
|
||||||
|
@ -233,14 +235,16 @@ class Read_EmbyServer():
|
||||||
"MediaSources"
|
"MediaSources"
|
||||||
)
|
)
|
||||||
result = doUtils(url, parameters=params)
|
result = doUtils(url, parameters=params)
|
||||||
|
try:
|
||||||
if result == "":
|
items['Items'].extend(result['Items'])
|
||||||
# Something happened to the connection.
|
except TypeError:
|
||||||
|
# Something happened to the connection
|
||||||
if not throttled:
|
if not throttled:
|
||||||
throttled = True
|
throttled = True
|
||||||
self.logMsg("Throttle activated.", 1)
|
self.logMsg("Throttle activated.", 1)
|
||||||
elif jump == highestjump:
|
|
||||||
# We already adjusted to highestjump, but it failed. Reset value
|
if jump == highestjump:
|
||||||
|
# We already tried with the highestjump, but it failed. Reset value.
|
||||||
self.logMsg("Reset highest value.", 1)
|
self.logMsg("Reset highest value.", 1)
|
||||||
highestjump = 0
|
highestjump = 0
|
||||||
|
|
||||||
|
@ -248,15 +252,16 @@ class Read_EmbyServer():
|
||||||
if highestjump:
|
if highestjump:
|
||||||
throttled = False
|
throttled = False
|
||||||
jump = highestjump
|
jump = highestjump
|
||||||
|
self.logMsg("Throttle deactivated.", 1)
|
||||||
else:
|
else:
|
||||||
jump = int(jump/2)
|
jump = int(jump/4)
|
||||||
|
self.logMsg("Set jump limit to recover: %s" % jump, 1)
|
||||||
|
|
||||||
self.logMsg("Set jump limit to recover: %s" % jump)
|
|
||||||
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("Server never came back online.")
|
self.logMsg("Unable to reconnect to server. Abort process.", 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
retry += 1
|
retry += 1
|
||||||
|
@ -264,9 +269,7 @@ class Read_EmbyServer():
|
||||||
# Abort was requested while waiting.
|
# Abort was requested while waiting.
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
xbmc.Monitor().waitForAbort(3)
|
# Request succeeded
|
||||||
else:
|
|
||||||
items['Items'].extend(result['Items'])
|
|
||||||
index += jump
|
index += jump
|
||||||
|
|
||||||
if dialog:
|
if dialog:
|
||||||
|
@ -278,17 +281,12 @@ class Read_EmbyServer():
|
||||||
highestjump = jump
|
highestjump = jump
|
||||||
|
|
||||||
if throttled:
|
if throttled:
|
||||||
# We needed to adjust the number, keep increasing until.
|
# We needed to adjust the number of item requested.
|
||||||
if jump < highestjump:
|
|
||||||
# Found a number that already works, use it.
|
|
||||||
throttled = False
|
|
||||||
jump = highestjump
|
|
||||||
self.logMsg("Throttle deactivated with jump limit set to: %s" % jump, 1)
|
|
||||||
else:
|
|
||||||
# keep increasing until the connection times out again
|
# keep increasing until the connection times out again
|
||||||
|
# to find the highest value
|
||||||
increment = int(jump*0.33)
|
increment = int(jump*0.33)
|
||||||
if not increment: # Incase the increment is 0
|
if not increment: # Incase the increment is 0
|
||||||
increment += 10
|
increment = 10
|
||||||
|
|
||||||
jump += increment
|
jump += increment
|
||||||
self.logMsg("Increase jump limit to: %s" % jump, 1)
|
self.logMsg("Increase jump limit to: %s" % jump, 1)
|
||||||
|
@ -305,7 +303,7 @@ class Read_EmbyServer():
|
||||||
else: # Views ungrouped
|
else: # Views ungrouped
|
||||||
url = "{server}/emby/Users/{UserId}/Items?Sortby=SortName&format=json"
|
url = "{server}/emby/Users/{UserId}/Items?Sortby=SortName&format=json"
|
||||||
|
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils(url)
|
||||||
try:
|
try:
|
||||||
items = result['Items']
|
items = result['Items']
|
||||||
|
|
||||||
|
@ -330,7 +328,7 @@ class Read_EmbyServer():
|
||||||
# Assumed missing is mixed then.
|
# Assumed missing is mixed then.
|
||||||
if itemtype is None:
|
if itemtype is None:
|
||||||
url = "{server}/emby/Library/MediaFolders?format=json"
|
url = "{server}/emby/Library/MediaFolders?format=json"
|
||||||
result = doUtils.downloadUrl(url)
|
result = doUtils(url)
|
||||||
|
|
||||||
for folder in result['Items']:
|
for folder in result['Items']:
|
||||||
if itemId == folder['Id']:
|
if itemId == folder['Id']:
|
||||||
|
@ -398,7 +396,7 @@ class Read_EmbyServer():
|
||||||
'IsVirtualUnaired': False,
|
'IsVirtualUnaired': False,
|
||||||
'Fields': "Etag"
|
'Fields': "Etag"
|
||||||
}
|
}
|
||||||
result = self.doUtils.downloadUrl(url, parameters=params)
|
result = self.doUtils(url, parameters=params)
|
||||||
if result:
|
if result:
|
||||||
items = result
|
items = result
|
||||||
|
|
||||||
|
@ -438,7 +436,7 @@ class Read_EmbyServer():
|
||||||
'Recursive': True,
|
'Recursive': True,
|
||||||
'Limit': 1
|
'Limit': 1
|
||||||
}
|
}
|
||||||
result = doUtils.downloadUrl(url, parameters=params)
|
result = doUtils(url, parameters=params)
|
||||||
try:
|
try:
|
||||||
total = result['TotalRecordCount']
|
total = result['TotalRecordCount']
|
||||||
items['TotalRecordCount'] = total
|
items['TotalRecordCount'] = total
|
||||||
|
@ -468,7 +466,7 @@ class Read_EmbyServer():
|
||||||
"AirTime,DateCreated,MediaStreams,People,ProviderIds,Overview"
|
"AirTime,DateCreated,MediaStreams,People,ProviderIds,Overview"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
result = doUtils.downloadUrl(url, parameters=params)
|
result = doUtils(url, parameters=params)
|
||||||
items['Items'].extend(result['Items'])
|
items['Items'].extend(result['Items'])
|
||||||
|
|
||||||
index += jump
|
index += jump
|
||||||
|
@ -510,7 +508,7 @@ class Read_EmbyServer():
|
||||||
}
|
}
|
||||||
|
|
||||||
url = "{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId
|
url = "{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId
|
||||||
result = self.doUtils.downloadUrl(url)
|
result = self.doUtils(url)
|
||||||
if result:
|
if result:
|
||||||
items = result
|
items = result
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue