Fixed plex companion headers and resume point

This commit is contained in:
tomkat83 2016-01-23 15:53:24 +01:00
parent 58020021fa
commit e0330c1a28
8 changed files with 102 additions and 80 deletions

View file

@ -90,7 +90,7 @@ class Main:
folderid = params['folderid'][0] folderid = params['folderid'][0]
modes[mode](itemid, folderid) modes[mode](itemid, folderid)
elif mode == "companion": elif mode == "companion":
resume = params.get('resume', '') resume = params.get('resume', '')[0]
modes[mode](itemid, resume=resume) modes[mode](itemid, resume=resume)
else: else:
modes[mode]() modes[mode]()

View file

@ -90,8 +90,6 @@ class PlexAPI():
self.userId = utils.window('emby_currUser') self.userId = utils.window('emby_currUser')
self.token = utils.window('emby_accessToken%s' % self.userId) self.token = utils.window('emby_accessToken%s' % self.userId)
self.server = utils.window('emby_server%s' % self.userId) self.server = utils.window('emby_server%s' % self.userId)
self.plexLogin = utils.settings('plexLogin')
self.plexToken = utils.settings('plexToken')
self.doUtils = downloadutils.DownloadUtils() self.doUtils = downloadutils.DownloadUtils()
@ -797,6 +795,8 @@ class PlexAPI():
""" """
# Get addon infos # Get addon infos
xargs = { xargs = {
"Content-type": "application/x-www-form-urlencoded",
"Access-Control-Allow-Origin": "*",
'X-Plex-Language': 'en', 'X-Plex-Language': 'en',
'X-Plex-Device': self.addonName, 'X-Plex-Device': self.addonName,
'X-Plex-Client-Platform': self.platform, 'X-Plex-Client-Platform': self.platform,
@ -1052,8 +1052,8 @@ class PlexAPI():
Will return empty strings if failed. Will return empty strings if failed.
""" """
plexLogin = self.plexLogin plexLogin = utils.settings('plexLogin')
plexToken = self.plexToken plexToken = utils.settings('plexToken')
machineIdentifier = utils.settings('plex_machineIdentifier') machineIdentifier = utils.settings('plex_machineIdentifier')
self.logMsg("Getting user list.", 1) self.logMsg("Getting user list.", 1)
# Get list of Plex home users # Get list of Plex home users
@ -1412,7 +1412,7 @@ class PlexAPI():
def GetPlexSectionResults(self, viewId, headerOptions={}): def GetPlexSectionResults(self, viewId, headerOptions={}):
""" """
Returns a list (raw JSON API dump) of all Plex items in the Plex Returns a list (raw JSON or XML API dump) of all Plex items in the Plex
section with key = viewId. section with key = viewId.
""" """
result = [] result = []
@ -1421,21 +1421,29 @@ class PlexAPI():
try: try:
result = jsondata['_children'] result = jsondata['_children']
except TypeError: except TypeError:
# Received an XML # Maybe we received an XML, check for that with tag attribute
pass try:
except: jsondata.tag
result = jsondata
# Nope, not an XML, abort
except AttributeError:
self.logMsg("Error retrieving all items for Plex section %s"
% viewId, -1)
return result
except KeyError:
self.logMsg("Error retrieving all items for Plex section %s" self.logMsg("Error retrieving all items for Plex section %s"
% viewId, -1) % viewId, -1)
return []
return result return result
def GetAllPlexLeaves(self, viewId, headerOptions={}): def GetAllPlexLeaves(self, viewId, headerOptions={}):
""" """
Returns a list (raw JSON API dump) of all Plex subitems for the key. Returns a list (raw JSON or XML API dump) of all Plex subitems for the
key.
(e.g. /library/sections/2/allLeaves pointing to all TV shows) (e.g. /library/sections/2/allLeaves pointing to all TV shows)
Input: Input:
viewId Id of Plex library, e.g. '2' viewId Id of Plex library, e.g. '2'
headerOptions to override the download headers
""" """
result = [] result = []
url = "{server}/library/sections/%s/allLeaves" % viewId url = "{server}/library/sections/%s/allLeaves" % viewId
@ -1443,12 +1451,18 @@ class PlexAPI():
try: try:
result = jsondata['_children'] result = jsondata['_children']
except TypeError: except TypeError:
# received an XMl # Maybe we received an XML, check for that with tag attribute
pass try:
jsondata.tag
result = jsondata
# Nope, not an XML, abort
except AttributeError:
self.logMsg("Error retrieving all leaves for Plex section %s"
% viewId, -1)
return result
except KeyError: except KeyError:
self.logMsg("Error retrieving all children for Plex viewId %s" self.logMsg("Error retrieving all leaves for Plex viewId %s"
% viewId, -1) % viewId, -1)
pass
return result return result
def GetAllPlexChildren(self, key): def GetAllPlexChildren(self, key):
@ -1471,10 +1485,12 @@ class PlexAPI():
def GetPlexMetadata(self, key): def GetPlexMetadata(self, key):
""" """
Returns raw API metadata for key. Returns raw API metadata for key as an etree XML.
Can be called with either Plex key '/library/metadata/xxxx'metadata Can be called with either Plex key '/library/metadata/xxxx'metadata
OR with the digits 'xxxx' only. OR with the digits 'xxxx' only.
Returns an empty string '' if something went wrong
""" """
xml = '' xml = ''
key = str(key) key = str(key)
@ -1495,9 +1511,13 @@ class PlexAPI():
url = url + '?' + urlencode(arguments) url = url + '?' + urlencode(arguments)
headerOptions = {'Accept': 'application/xml'} headerOptions = {'Accept': 'application/xml'}
xml = self.doUtils.downloadUrl(url, headerOptions=headerOptions) xml = self.doUtils.downloadUrl(url, headerOptions=headerOptions)
if not xml: # Did we receive a valid XML?
try:
xml.tag
# Nope we did not receive a valid XML
except AttributeError:
self.logMsg("Error retrieving metadata for %s" % url, -1) self.logMsg("Error retrieving metadata for %s" % url, -1)
xml = None xml = ''
return xml return xml
@ -1534,7 +1554,6 @@ class API():
Which child in the XML response shall we look at and work with? Which child in the XML response shall we look at and work with?
""" """
self.child = int(number) self.child = int(number)
self.logMsg("Set child number to %s" % number, 1)
def getChildNumber(self): def getChildNumber(self):
""" """
@ -1592,7 +1611,7 @@ class API():
def getChecksum(self): def getChecksum(self):
""" """
Can be used on both XML and JSON Can be used on both XML and JSON
Returns a string, not int! Returns a string, not int
""" """
item = self.item item = self.item
# XML # XML

View file

@ -80,15 +80,12 @@ class PlexCompanion(threading.Thread):
message_count += 1 message_count += 1
if message_count > 30: if message_count > 30:
if self.stopped():
break
if self.client.check_client_registration(): if self.client.check_client_registration():
self.logMsg("Client is still registered", 1) self.logMsg("Client is still registered", 1)
else: else:
self.logMsg("Client is no longer registered", self.logMsg("Client is no longer registered", 1)
1) self.logMsg("PlexBMC Helper still running on port %s"
self.logMsg("PlexBMC Helper still running on " % self.port, 1)
"port %s" % self.port, 1)
message_count = 0 message_count = 0
if not is_running: if not is_running:

View file

@ -32,7 +32,7 @@ import embydb_functions
################################################################################################# #################################################################################################
def plexCompanion(fullurl, resume=""): def plexCompanion(fullurl, resume=None):
regex = re.compile(r'''/(\d+)$''') regex = re.compile(r'''/(\d+)$''')
itemid = regex.findall(fullurl) itemid = regex.findall(fullurl)
try: try:
@ -49,9 +49,12 @@ def plexCompanion(fullurl, resume=""):
# Get dbid using itemid # Get dbid using itemid
dbid = emby.getItem_byId(itemid)[0] dbid = emby.getItem_byId(itemid)[0]
embyconn.close() embyconn.close()
# Fix resume timing
if resume:
resume = round(float(resume) / 1000.0, 6)
# Start playing # Start playing
item = PlexAPI.PlexAPI().GetPlexMetadata(itemid) item = PlexAPI.PlexAPI().GetPlexMetadata(itemid)
pbutils.PlaybackUtils(item).play(itemid, dbid) pbutils.PlaybackUtils(item).play(itemid, dbid, seektime=resume)
def doPlayback(itemid, dbid): def doPlayback(itemid, dbid):

View file

@ -270,7 +270,7 @@ class Movies(Items):
Plex resume points for movies in progress. Plex resume points for movies in progress.
""" """
API = PlexAPI.API(itemList) API = PlexAPI.API(itemList)
for itemNumber in range(0, len(itemList)): for itemNumber in range(len(itemList)):
API.setChildNumber(itemNumber) API.setChildNumber(itemNumber)
itemid = API.getKey() itemid = API.getKey()
# Get key and db entry on the Kodi db side # Get key and db entry on the Kodi db side
@ -890,7 +890,7 @@ class TVShows(Items):
Plex resume points for movies in progress. Plex resume points for movies in progress.
""" """
API = PlexAPI.API(itemList) API = PlexAPI.API(itemList)
for itemNumber in range(0, len(itemList)): for itemNumber in range(len(itemList)):
API.setChildNumber(itemNumber) API.setChildNumber(itemNumber)
itemid = API.getKey() itemid = API.getKey()
# Get key and db entry on the Kodi db side # Get key and db entry on the Kodi db side

View file

@ -64,15 +64,12 @@ class ThreadedGetMetadata(threading.Thread):
except: except:
raise raise
# check whether valid XML # check whether valid XML
try: if plexXML:
# .tag works for XML
plexXML.tag
updateItem['XML'] = plexXML updateItem['XML'] = plexXML
# place item into out queue # place item into out queue
self.out_queue.put(updateItem) self.out_queue.put(updateItem)
# If we don't have a valid XML, don't put that into the queue # If we don't have a valid XML, don't put that into the queue
except AttributeError: # but skip this item for now
pass
# Keep track of where we are at # Keep track of where we are at
with self.lock: with self.lock:
getMetadataCount += 1 getMetadataCount += 1
@ -128,11 +125,9 @@ class ThreadedProcessMetadata(threading.Thread):
title = updateItem['title'] title = updateItem['title']
itemSubFkt = getattr(item, method) itemSubFkt = getattr(item, method)
with self.lock: with self.lock:
itemSubFkt( itemSubFkt(plexitem,
plexitem, viewtag=viewName,
viewtag=viewName, viewid=viewId)
viewid=viewId
)
# Keep track of where we are at # Keep track of where we are at
processMetadataCount += 1 processMetadataCount += 1
processingViewName = title processingViewName = title
@ -170,11 +165,9 @@ class ThreadedShowSyncInfo(threading.Thread):
total = self.total total = self.total
downloadLock = self.locks[0] downloadLock = self.locks[0]
processLock = self.locks[1] processLock = self.locks[1]
self.dialog.create( self.dialog.create("%s: Sync %s: %s items"
"%s: Sync %s: %s items" % (self.addonName, % (self.addonName, self.itemType, str(total)),
self.itemType, "Starting")
str(total)),
"Starting")
global getMetadataCount global getMetadataCount
global processMetadataCount global processMetadataCount
global processingViewName global processingViewName
@ -191,11 +184,11 @@ class ThreadedShowSyncInfo(threading.Thread):
percentage = int(float(totalProgress) / float(total)*100.0) percentage = int(float(totalProgress) / float(total)*100.0)
except ZeroDivisionError: except ZeroDivisionError:
percentage = 0 percentage = 0
self.dialog.update( self.dialog.update(percentage,
percentage, message="Downloaded: %s, Processed: %s: %s"
message="Downloaded: %s, Processed: %s: %s" % ( % (getMetadataProgress,
getMetadataProgress, processMetadataProgress, viewName) processMetadataProgress, viewName))
) # Sleep for x milliseconds
xbmc.sleep(500) xbmc.sleep(500)
self.dialog.close() self.dialog.close()
@ -430,11 +423,11 @@ class LibrarySync(threading.Thread):
utils.window('emby_dbScan', clear=True) utils.window('emby_dbScan', clear=True)
utils.window('emby_initialScan', clear=True) utils.window('emby_initialScan', clear=True)
xbmcgui.Dialog().notification( xbmcgui.Dialog().notification(
heading=self.addonName, heading=self.addonName,
message="%s completed in: %s" % message="%s completed in: %s"
(message, str(elapsedtotal).split('.')[0]), % (message, str(elapsedtotal).split('.')[0]),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
sound=False) sound=False)
return True return True
def maintainViews(self): def maintainViews(self):
@ -567,22 +560,25 @@ class LibrarySync(threading.Thread):
Adds items to self.updatelist as well as self.allPlexElementsId dict Adds items to self.updatelist as well as self.allPlexElementsId dict
Input: Input:
elementList: List of elements, e.g. a list of '_children' elementList: List of elements, e.g. list of '_children'
movie elements as received via JSON from PMS movie elements as received from PMS
itemType: 'Movies', 'TVShows', ...
method: Method name to be called with this itemtype
see itemtypes.py
viewName: Name of the Plex view (e.g. 'My TV shows')
viewId: Id/Key of Plex library (e.g. '1')
Output: self.updatelist, self.allPlexElementsId Output: self.updatelist, self.allPlexElementsId
self.updatelist APPENDED(!!) list itemids (Plex Keys as self.updatelist APPENDED(!!) list itemids (Plex Keys as
as received from API.getKey()) as received from API.getKey())
= [ One item in this list is of the form:
{ 'itemId': xxx,
'itemId': xxx, 'itemType': 'Movies','TVShows', ...
'itemType': 'Movies'/'TVShows'/..., 'method': 'add_update', 'add_updateSeason', ...
'method': 'add_update', 'add_updateSeason', ... 'viewName': xxx,
'viewName': xxx, 'viewId': xxx
'viewId': xxx
}, ... self.allPlexElementsId APPENDED(!!) dict
]
self.allPlexElementsId APPENDED(!!) dic
= {itemid: checksum} = {itemid: checksum}
""" """
if self.compare: if self.compare:
@ -635,6 +631,7 @@ class LibrarySync(threading.Thread):
by then calling itemtypes.<itemType>() by then calling itemtypes.<itemType>()
Input: Input:
itemType: 'Movies', 'TVShows', ...
self.updatelist self.updatelist
""" """
# Some logging, just in case. # Some logging, just in case.
@ -672,7 +669,6 @@ class LibrarySync(threading.Thread):
thread.start() thread.start()
threads.append(thread) threads.append(thread)
self.logMsg("%s download threads spawned" % self.syncThreadNumber, 1) self.logMsg("%s download threads spawned" % self.syncThreadNumber, 1)
self.logMsg("Queue populated", 1)
# Spawn one more thread to process Metadata, once downloaded # Spawn one more thread to process Metadata, once downloaded
thread = ThreadedProcessMetadata(processMetadataQueue, thread = ThreadedProcessMetadata(processMetadataQueue,
itemType, itemType,
@ -783,17 +779,17 @@ class LibrarySync(threading.Thread):
""" """
starttotal = datetime.now() starttotal = datetime.now()
plx = PlexAPI.PlexAPI() plx = PlexAPI.PlexAPI()
# Download XML, not JSON # Download XML, not JSON, because PMS JSON seems to be damaged
headerOptions = {'Accept': 'application/xml'} headerOptions = {'Accept': 'application/xml'}
plexItems = plx.GetAllPlexLeaves(viewId, plexItems = plx.GetAllPlexLeaves(viewId,
headerOptions=headerOptions) headerOptions=headerOptions)
itemMth = getattr(itemtypes, itemType) itemMth = getattr(itemtypes, itemType)
with itemMth() as method: with itemMth() as method:
method.UpdateWatched(plexItems) method.updateUserdata(plexItems)
elapsedtotal = datetime.now() - starttotal elapsedtotal = datetime.now() - starttotal
self.logMsg("Syncing userdata for itemtype %s and viewid %s took " self.logMsg("Syncing userdata for itemtype %s and viewid %s took "
"%s seconds" % (itemType, viewId, elapsedtotal), 0) "%s seconds" % (itemType, viewId, elapsedtotal), 1)
def musicvideos(self, embycursor, kodicursor, pdialog): def musicvideos(self, embycursor, kodicursor, pdialog):
# Get musicvideos from emby # Get musicvideos from emby
@ -877,6 +873,7 @@ class LibrarySync(threading.Thread):
self.allKodiElementsId.update(all_kodiepisodes) self.allKodiElementsId.update(all_kodiepisodes)
except ValueError: except ValueError:
pass pass
# Close DB connections
embyconn.close() embyconn.close()
##### PROCESS TV Shows ##### ##### PROCESS TV Shows #####

View file

@ -47,7 +47,7 @@ class PlaybackUtils():
self.className = self.__class__.__name__ self.className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl) utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
def play(self, itemid, dbid=None): def play(self, itemid, dbid=None, seektime=None):
self.logMsg("Play called.", 1) self.logMsg("Play called.", 1)
@ -89,8 +89,9 @@ class PlaybackUtils():
############### RESUME POINT ################ ############### RESUME POINT ################
userdata = API.getUserData() if seektime is None:
seektime = userdata['Resume'] userdata = API.getUserData()
seektime = userdata['Resume']
# We need to ensure we add the intro and additional parts only once. # We need to ensure we add the intro and additional parts only once.
# Otherwise we get a loop. # Otherwise we get a loop.

View file

@ -200,12 +200,17 @@ class Subscriber:
printDebug("sending xml to subscriber %s: %s" % (self.tostr(), msg)) printDebug("sending xml to subscriber %s: %s" % (self.tostr(), msg))
url = self.protocol + '://' + self.host + ':' + self.port \ url = self.protocol + '://' + self.host + ':' + self.port \
+ "/:/timeline" + "/:/timeline"
response = self.download.downloadUrl(url, # Override some headers
postBody=msg, headerOptions = {
type="POSTXML") 'Accept': '*/*'
}
response = self.download.downloadUrl(
url,
postBody=msg,
type="POSTXML",
headerOptions=headerOptions)
# if not requests.post(self.host, self.port, "/:/timeline", msg, getPlexHeaders(), self.protocol): # if not requests.post(self.host, self.port, "/:/timeline", msg, getPlexHeaders(), self.protocol):
# subMgr.removeSubscriber(self.uuid) # subMgr.removeSubscriber(self.uuid)
if response in [False, 401]: if response in [False, 401]:
subMgr.removeSubscriber(self.uuid) subMgr.removeSubscriber(self.uuid)
subMgr = SubscriptionManager() subMgr = SubscriptionManager()