Added UpdateUserdata to Movies and TV Shows
This commit is contained in:
parent
eddfa23a71
commit
40c02395a2
3 changed files with 108 additions and 121 deletions
|
@ -1412,37 +1412,44 @@ class PlexAPI():
|
||||||
})
|
})
|
||||||
return collections
|
return collections
|
||||||
|
|
||||||
def GetPlexSectionResults(self, viewId):
|
def GetPlexSectionResults(self, viewId, headerOptions={}):
|
||||||
"""
|
"""
|
||||||
Returns a list (raw JSON API dump) of all Plex items in the Plex
|
Returns a list (raw JSON API dump) of all Plex items in the Plex
|
||||||
section with key = viewId.
|
section with key = viewId.
|
||||||
"""
|
"""
|
||||||
result = []
|
result = []
|
||||||
url = "{server}/library/sections/%s/all" % viewId
|
url = "{server}/library/sections/%s/all" % viewId
|
||||||
jsondata = self.doUtils.downloadUrl(url)
|
jsondata = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
|
||||||
try:
|
try:
|
||||||
result = jsondata['_children']
|
result = jsondata['_children']
|
||||||
except KeyError:
|
except TypeError:
|
||||||
self.logMsg("Error retrieving all items for Plex section %s" % viewId, -1)
|
# Received an XML
|
||||||
pass
|
pass
|
||||||
|
except:
|
||||||
|
self.logMsg("Error retrieving all items for Plex section %s"
|
||||||
|
% viewId, -1)
|
||||||
|
return []
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def GetAllPlexLeaves(self, key):
|
def GetAllPlexLeaves(self, viewId, headerOptions={}):
|
||||||
"""
|
"""
|
||||||
Returns a list (raw JSON API dump) of all Plex subitems for the key.
|
Returns a list (raw JSON API dump) of all Plex subitems for the key.
|
||||||
(e.g. /library/metadata/194853/allLeaves pointing to all episodes
|
(e.g. /library/sections/2/allLeaves pointing to all TV shows)
|
||||||
of a TV show)
|
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
key Key to a Plex item, e.g. 12345
|
viewId Id of Plex library, e.g. '2'
|
||||||
"""
|
"""
|
||||||
result = []
|
result = []
|
||||||
url = "{server}/library/metadata/%s/allLeaves" % key
|
url = "{server}/library/sections/%s/allLeaves" % viewId
|
||||||
jsondata = self.doUtils.downloadUrl(url)
|
jsondata = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
|
||||||
try:
|
try:
|
||||||
result = jsondata['_children']
|
result = jsondata['_children']
|
||||||
|
except TypeError:
|
||||||
|
# received an XMl
|
||||||
|
pass
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.logMsg("Error retrieving all children for Plex item %s" % key, -1)
|
self.logMsg("Error retrieving all children for Plex viewId %s"
|
||||||
|
% viewId, -1)
|
||||||
pass
|
pass
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -1517,6 +1524,8 @@ class API():
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = utils.window('emby_server%s' % self.userId)
|
||||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
self.token = utils.window('emby_accessToken%s' % self.userId)
|
||||||
|
|
||||||
|
self.jumpback = int(utils.settings('resumeJumpBack'))
|
||||||
|
|
||||||
def logMsg(self, msg, lvl=1):
|
def logMsg(self, msg, lvl=1):
|
||||||
className = self.__class__.__name__
|
className = self.__class__.__name__
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||||
|
@ -1643,6 +1652,7 @@ class API():
|
||||||
'Played': played, # True/False
|
'Played': played, # True/False
|
||||||
'LastPlayedDate': lastPlayedDate,
|
'LastPlayedDate': lastPlayedDate,
|
||||||
'Resume': resume, # Resume time in seconds
|
'Resume': resume, # Resume time in seconds
|
||||||
|
'Runtime': runtime,
|
||||||
'Rating': rating
|
'Rating': rating
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
@ -1670,17 +1680,14 @@ class API():
|
||||||
except KeyError:
|
except KeyError:
|
||||||
lastPlayedDate = None
|
lastPlayedDate = None
|
||||||
|
|
||||||
try:
|
resume, runtime = self.getRuntime()
|
||||||
resume = float(item['viewOffset']) * 1.0/1000.0
|
|
||||||
resume = round(resume, 6)
|
|
||||||
except KeyError:
|
|
||||||
resume = 0.0
|
|
||||||
return {
|
return {
|
||||||
'Favorite': favorite,
|
'Favorite': favorite,
|
||||||
'PlayCount': playcount,
|
'PlayCount': playcount,
|
||||||
'Played': played,
|
'Played': played,
|
||||||
'LastPlayedDate': lastPlayedDate,
|
'LastPlayedDate': lastPlayedDate,
|
||||||
'Resume': resume,
|
'Resume': resume,
|
||||||
|
'Runtime': runtime,
|
||||||
'Rating': rating
|
'Rating': rating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1890,6 +1897,14 @@ class API():
|
||||||
resume = float(item['viewOffset'])
|
resume = float(item['viewOffset'])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
resume = 0.0
|
resume = 0.0
|
||||||
|
|
||||||
|
# Adjust the resume point by x seconds as chosen by the user in the
|
||||||
|
# settings
|
||||||
|
if resume:
|
||||||
|
# To avoid negative bookmark
|
||||||
|
if resume > self.jumpback:
|
||||||
|
resume = resume - self.jumpback
|
||||||
|
|
||||||
runtime = runtime * time_factor
|
runtime = runtime * time_factor
|
||||||
resume = resume * time_factor
|
resume = resume * time_factor
|
||||||
resume = round(resume, 6)
|
resume = round(resume, 6)
|
||||||
|
@ -2465,16 +2480,6 @@ class API():
|
||||||
urlencode(args)
|
urlencode(args)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def adjustResume(self, resume_seconds):
|
|
||||||
resume = 0
|
|
||||||
if resume_seconds:
|
|
||||||
resume = round(float(resume_seconds), 6)
|
|
||||||
jumpback = int(utils.settings('resumeJumpBack'))
|
|
||||||
if resume > jumpback:
|
|
||||||
# To avoid negative bookmark
|
|
||||||
resume = resume - jumpback
|
|
||||||
return resume
|
|
||||||
|
|
||||||
def externalSubs(self, playurl):
|
def externalSubs(self, playurl):
|
||||||
externalsubs = []
|
externalsubs = []
|
||||||
mapping = {}
|
mapping = {}
|
||||||
|
|
|
@ -281,6 +281,26 @@ class Movies(Items):
|
||||||
count += 1
|
count += 1
|
||||||
self.add_updateBoxset(boxset)
|
self.add_updateBoxset(boxset)
|
||||||
|
|
||||||
|
def updateUserdata(self, itemList):
|
||||||
|
"""
|
||||||
|
Updates the Kodi watched state of the item from PMS. Also retrieves
|
||||||
|
Plex resume points for movies in progress.
|
||||||
|
"""
|
||||||
|
API = PlexAPI.API(itemList)
|
||||||
|
for itemNumber in range(0, len(itemList)):
|
||||||
|
API.setChildNumber(itemNumber)
|
||||||
|
itemid = API.getKey()
|
||||||
|
# Get key and db entry on the Kodi db side
|
||||||
|
fileid = self.emby_db.getItem_byId(itemid)[1]
|
||||||
|
# Grab the user's viewcount, resume points etc. from PMS' answer
|
||||||
|
userdata = API.getUserData()
|
||||||
|
# Write to Kodi DB
|
||||||
|
self.kodi_db.addPlaystate(fileid,
|
||||||
|
userdata['Resume'],
|
||||||
|
userdata['Runtime'],
|
||||||
|
userdata['PlayCount'],
|
||||||
|
userdata['LastPlayedDate'])
|
||||||
|
|
||||||
def add_update(self, item, viewtag=None, viewid=None):
|
def add_update(self, item, viewtag=None, viewid=None):
|
||||||
# Process single movie
|
# Process single movie
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
|
@ -485,48 +505,8 @@ class Movies(Items):
|
||||||
# tags.append("Favorite movies")
|
# tags.append("Favorite movies")
|
||||||
kodi_db.addTags(movieid, tags, "movie")
|
kodi_db.addTags(movieid, tags, "movie")
|
||||||
# Process playstates
|
# Process playstates
|
||||||
resume = API.adjustResume(userdata['Resume'])
|
|
||||||
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
|
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
|
||||||
|
|
||||||
def updateUserdata(self, item):
|
|
||||||
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
|
||||||
# Poster with progress bar
|
|
||||||
emby_db = self.emby_db
|
|
||||||
kodi_db = self.kodi_db
|
|
||||||
API = api.API(item)
|
|
||||||
|
|
||||||
# Get emby information
|
|
||||||
itemid = item['Id']
|
|
||||||
checksum = API.getChecksum()
|
|
||||||
userdata = API.getUserData()
|
|
||||||
runtime = API.getRuntime()
|
|
||||||
|
|
||||||
# Get Kodi information
|
|
||||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
|
||||||
try:
|
|
||||||
movieid = emby_dbitem[0]
|
|
||||||
fileid = emby_dbitem[1]
|
|
||||||
self.logMsg(
|
|
||||||
"Update playstate for movie: %s fileid: %s"
|
|
||||||
% (item['Name'], fileid), 1)
|
|
||||||
except TypeError:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Process favorite tags
|
|
||||||
if userdata['Favorite']:
|
|
||||||
kodi_db.addTag(movieid, "Favorite movies", "movie")
|
|
||||||
else:
|
|
||||||
kodi_db.removeTag(movieid, "Favorite movies", "movie")
|
|
||||||
|
|
||||||
# Process playstates
|
|
||||||
playcount = userdata['PlayCount']
|
|
||||||
dateplayed = userdata['LastPlayedDate']
|
|
||||||
resume = API.adjustResume(userdata['Resume'])
|
|
||||||
total = round(float(runtime), 6)
|
|
||||||
|
|
||||||
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
|
||||||
emby_db.updateReference(itemid, checksum)
|
|
||||||
|
|
||||||
def remove(self, itemid):
|
def remove(self, itemid):
|
||||||
# Remove movieid, fileid, emby reference
|
# Remove movieid, fileid, emby reference
|
||||||
emby_db = self.emby_db
|
emby_db = self.emby_db
|
||||||
|
@ -1171,6 +1151,26 @@ class TVShows(Items):
|
||||||
if not pdialog and self.contentmsg:
|
if not pdialog and self.contentmsg:
|
||||||
self.contentPop(title)
|
self.contentPop(title)
|
||||||
|
|
||||||
|
def updateUserdata(self, itemList):
|
||||||
|
"""
|
||||||
|
Updates the Kodi watched state of the item from PMS. Also retrieves
|
||||||
|
Plex resume points for movies in progress.
|
||||||
|
"""
|
||||||
|
API = PlexAPI.API(itemList)
|
||||||
|
for itemNumber in range(0, len(itemList)):
|
||||||
|
API.setChildNumber(itemNumber)
|
||||||
|
itemid = API.getKey()
|
||||||
|
# Get key and db entry on the Kodi db side
|
||||||
|
fileid = self.emby_db.getItem_byId(itemid)[1]
|
||||||
|
# Grab the user's viewcount, resume points etc. from PMS' answer
|
||||||
|
userdata = API.getUserData()
|
||||||
|
# Write to Kodi DB
|
||||||
|
self.kodi_db.addPlaystate(fileid,
|
||||||
|
userdata['Resume'],
|
||||||
|
userdata['Runtime'],
|
||||||
|
userdata['PlayCount'],
|
||||||
|
userdata['LastPlayedDate'])
|
||||||
|
|
||||||
def add_update(self, item, viewtag=None, viewid=None):
|
def add_update(self, item, viewtag=None, viewid=None):
|
||||||
# Process single tvshow
|
# Process single tvshow
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
|
@ -1611,9 +1611,7 @@ class TVShows(Items):
|
||||||
streams = API.getMediaStreams()
|
streams = API.getMediaStreams()
|
||||||
kodi_db.addStreams(fileid, streams, runtime)
|
kodi_db.addStreams(fileid, streams, runtime)
|
||||||
# Process playstates
|
# Process playstates
|
||||||
resume = API.adjustResume(userdata['Resume'])
|
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
|
||||||
total = round(float(runtime), 6)
|
|
||||||
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
|
||||||
if not self.directpath and resume:
|
if not self.directpath and resume:
|
||||||
# Create additional entry for widgets. This is only required for plugin/episode.
|
# Create additional entry for widgets. This is only required for plugin/episode.
|
||||||
temppathid = kodi_db.getPath("plugin://plugin.video.plexkodiconnect.tvshows/")
|
temppathid = kodi_db.getPath("plugin://plugin.video.plexkodiconnect.tvshows/")
|
||||||
|
@ -1625,53 +1623,7 @@ class TVShows(Items):
|
||||||
"WHERE idFile = ?"
|
"WHERE idFile = ?"
|
||||||
))
|
))
|
||||||
kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid))
|
kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid))
|
||||||
kodi_db.addPlaystate(tempfileid, resume, total, playcount, dateplayed)
|
kodi_db.addPlaystate(tempfileid, resume, runtime, playcount, dateplayed)
|
||||||
|
|
||||||
def updateUserdata(self, item):
|
|
||||||
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
|
||||||
# Poster with progress bar
|
|
||||||
emby_db = self.emby_db
|
|
||||||
kodi_db = self.kodi_db
|
|
||||||
API = api.API(item)
|
|
||||||
|
|
||||||
# Get emby information
|
|
||||||
itemid = item['Id']
|
|
||||||
checksum = API.getChecksum()
|
|
||||||
userdata = API.getUserData()
|
|
||||||
runtime = API.getRuntime()
|
|
||||||
|
|
||||||
# Get Kodi information
|
|
||||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
|
||||||
try:
|
|
||||||
kodiid = emby_dbitem[0]
|
|
||||||
fileid = emby_dbitem[1]
|
|
||||||
mediatype = emby_dbitem[4]
|
|
||||||
self.logMsg(
|
|
||||||
"Update playstate for %s: %s fileid: %s"
|
|
||||||
% (mediatype, item['Name'], fileid), 1)
|
|
||||||
except TypeError:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Process favorite tags
|
|
||||||
if mediatype == "tvshow":
|
|
||||||
if userdata['Favorite']:
|
|
||||||
kodi_db.addTag(kodiid, "Favorite tvshows", "tvshow")
|
|
||||||
else:
|
|
||||||
kodi_db.removeTag(kodiid, "Favorite tvshows", "tvshow")
|
|
||||||
|
|
||||||
# Process playstates
|
|
||||||
if mediatype == "episode":
|
|
||||||
playcount = userdata['PlayCount']
|
|
||||||
dateplayed = userdata['LastPlayedDate']
|
|
||||||
resume = API.adjustResume(userdata['Resume'])
|
|
||||||
total = round(float(runtime), 6)
|
|
||||||
|
|
||||||
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
|
||||||
if not self.directpath and not resume:
|
|
||||||
# Make sure there's no other bookmarks created by widget.
|
|
||||||
filename = kodi_db.getFile(fileid)
|
|
||||||
kodi_db.removeFile("plugin://plugin.video.plexkodiconnect.tvshows/", filename)
|
|
||||||
emby_db.updateReference(itemid, checksum)
|
|
||||||
|
|
||||||
def remove(self, itemid):
|
def remove(self, itemid):
|
||||||
# Remove showid, fileid, pathid, emby reference
|
# Remove showid, fileid, pathid, emby reference
|
||||||
|
|
|
@ -302,7 +302,6 @@ class LibrarySync(threading.Thread):
|
||||||
# Save last sync time
|
# Save last sync time
|
||||||
overlap = 2
|
overlap = 2
|
||||||
|
|
||||||
self.logMsg("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"
|
self.logMsg("New sync time: client time -%s min: %s"
|
||||||
|
@ -755,6 +754,10 @@ class LibrarySync(threading.Thread):
|
||||||
viewId)
|
viewId)
|
||||||
self.GetAndProcessXMLs(itemType)
|
self.GetAndProcessXMLs(itemType)
|
||||||
self.logMsg("Processed view %s with ID %s" % (viewName, viewId), 1)
|
self.logMsg("Processed view %s with ID %s" % (viewName, viewId), 1)
|
||||||
|
# Update viewstate
|
||||||
|
for view in views:
|
||||||
|
self.PlexUpdateWatched(view['id'], itemType)
|
||||||
|
|
||||||
##### PROCESS DELETES #####
|
##### PROCESS DELETES #####
|
||||||
if self.compare:
|
if self.compare:
|
||||||
# Manual sync, process deletes
|
# Manual sync, process deletes
|
||||||
|
@ -765,6 +768,27 @@ class LibrarySync(threading.Thread):
|
||||||
self.logMsg("%s sync is finished." % itemType, 1)
|
self.logMsg("%s sync is finished." % itemType, 1)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def PlexUpdateWatched(self, viewId, itemType):
|
||||||
|
"""
|
||||||
|
Updates ALL plex elements' view status ('watched' or 'unwatched') and
|
||||||
|
also updates resume times.
|
||||||
|
|
||||||
|
This is done by downloading one XML for ALL elements with viewId
|
||||||
|
"""
|
||||||
|
starttotal = datetime.now()
|
||||||
|
plx = PlexAPI.PlexAPI()
|
||||||
|
# Download XML, not JSON
|
||||||
|
headerOptions = {'Accept': 'application/xml'}
|
||||||
|
plexItems = plx.GetAllPlexLeaves(viewId,
|
||||||
|
headerOptions=headerOptions)
|
||||||
|
itemMth = getattr(itemtypes, itemType)
|
||||||
|
with itemMth() as method:
|
||||||
|
method.UpdateWatched(plexItems)
|
||||||
|
|
||||||
|
elapsedtotal = datetime.now() - starttotal
|
||||||
|
self.logMsg("Syncing userdata for itemtype %s and viewid %s took "
|
||||||
|
"%s seconds" % (itemType, viewId, elapsedtotal), 0)
|
||||||
|
|
||||||
def musicvideos(self, embycursor, kodicursor, pdialog, compare=False):
|
def musicvideos(self, embycursor, kodicursor, pdialog, compare=False):
|
||||||
# Get musicvideos from emby
|
# Get musicvideos from emby
|
||||||
emby = self.emby
|
emby = self.emby
|
||||||
|
@ -1022,22 +1046,24 @@ class LibrarySync(threading.Thread):
|
||||||
'add_updateSeason',
|
'add_updateSeason',
|
||||||
None,
|
None,
|
||||||
tvShowId) # send showId instead of viewid
|
tvShowId) # send showId instead of viewid
|
||||||
self.logMsg("Analyzed all seasons of TV show with Plex Id %s" % tvShowId, 1)
|
self.logMsg("Analyzed all seasons of TV show with Plex Id %s"
|
||||||
|
% tvShowId, 1)
|
||||||
|
|
||||||
##### PROCESS TV Episodes #####
|
##### PROCESS TV Episodes #####
|
||||||
# Cycle through tv shows
|
# Cycle through tv shows
|
||||||
for tvShowId in allPlexTvShowsId:
|
for view in views:
|
||||||
if self.shouldStop():
|
if self.shouldStop():
|
||||||
return False
|
return False
|
||||||
# Grab all episodes to tvshow from PMS
|
# Grab all episodes to tvshow from PMS
|
||||||
episodes = plx.GetAllPlexLeaves(tvShowId)
|
episodes = plx.GetAllPlexLeaves(view['id'])
|
||||||
# Populate self.updatelist and self.allPlexElementsId
|
# Populate self.updatelist and self.allPlexElementsId
|
||||||
self.GetUpdatelist(episodes,
|
self.GetUpdatelist(episodes,
|
||||||
itemType,
|
itemType,
|
||||||
'add_updateEpisode',
|
'add_updateEpisode',
|
||||||
None,
|
None,
|
||||||
None)
|
None)
|
||||||
self.logMsg("Analyzed all episodes of TV show with Plex Id %s" % tvShowId, 1)
|
self.logMsg("Analyzed all episodes of TV show with Plex Id %s"
|
||||||
|
% tvShowId, 1)
|
||||||
|
|
||||||
# Process self.updatelist
|
# Process self.updatelist
|
||||||
self.GetAndProcessXMLs(itemType)
|
self.GetAndProcessXMLs(itemType)
|
||||||
|
@ -1050,6 +1076,10 @@ class LibrarySync(threading.Thread):
|
||||||
TVshow.refreshSeasonEntry(XMLtvshow, tvShowId)
|
TVshow.refreshSeasonEntry(XMLtvshow, tvShowId)
|
||||||
self.logMsg("Season info refreshed", 1)
|
self.logMsg("Season info refreshed", 1)
|
||||||
|
|
||||||
|
# Update viewstate:
|
||||||
|
for view in views:
|
||||||
|
self.PlexUpdateWatched(view['id'], itemType)
|
||||||
|
|
||||||
##### PROCESS DELETES #####
|
##### PROCESS DELETES #####
|
||||||
if self.compare:
|
if self.compare:
|
||||||
# Manual sync, process deletes
|
# Manual sync, process deletes
|
||||||
|
|
Loading…
Reference in a new issue