Overhaul PlexAPI. Only using XMLs now, no JSONs
This commit is contained in:
parent
dc44f1a879
commit
fa0003a5eb
6 changed files with 175 additions and 330 deletions
|
@ -769,13 +769,12 @@ class PlexAPI():
|
||||||
XML = self.getXMLFromPMS(PMS['baseURL'],PMS['path'],PMS['options'],PMS['token'])
|
XML = self.getXMLFromPMS(PMS['baseURL'],PMS['path'],PMS['options'],PMS['token'])
|
||||||
queue.put( (PMS['data'], XML) )
|
queue.put( (PMS['data'], XML) )
|
||||||
|
|
||||||
def getXArgsDeviceInfo(self, options={}, JSON=False):
|
def getXArgsDeviceInfo(self, options={}):
|
||||||
"""
|
"""
|
||||||
Returns a dictionary that can be used as headers for GET and POST
|
Returns a dictionary that can be used as headers for GET and POST
|
||||||
requests. An authentication option is NOT yet added.
|
requests. An authentication option is NOT yet added.
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
JSON=True will enforce a JSON answer
|
|
||||||
options: dictionary of options that will override the
|
options: dictionary of options that will override the
|
||||||
standard header options otherwise set.
|
standard header options otherwise set.
|
||||||
Output:
|
Output:
|
||||||
|
@ -800,8 +799,6 @@ class PlexAPI():
|
||||||
|
|
||||||
if self.token:
|
if self.token:
|
||||||
xargs['X-Plex-Token'] = self.token
|
xargs['X-Plex-Token'] = self.token
|
||||||
if JSON:
|
|
||||||
xargs['Accept'] = 'application/json'
|
|
||||||
if options:
|
if options:
|
||||||
xargs.update(options)
|
xargs.update(options)
|
||||||
return xargs
|
return xargs
|
||||||
|
@ -1381,8 +1378,6 @@ 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 setPartNumber(self, number=0):
|
def setPartNumber(self, number=0):
|
||||||
"""
|
"""
|
||||||
Sets the part number to work with (used to deal with Movie with several
|
Sets the part number to work with (used to deal with Movie with several
|
||||||
|
@ -1396,10 +1391,10 @@ class API():
|
||||||
"""
|
"""
|
||||||
return self.part
|
return self.part
|
||||||
|
|
||||||
def convert_date(self, stamp):
|
def DateToKodi(self, stamp):
|
||||||
"""
|
"""
|
||||||
convert_date(stamp) converts a Unix time stamp (seconds passed since
|
converts a Unix time stamp (seconds passed sinceJanuary 1 1970) to a
|
||||||
January 1 1970) to a propper, human-readable time stamp
|
propper, human-readable time stamp used by Kodi
|
||||||
"""
|
"""
|
||||||
# DATEFORMAT = xbmc.getRegion('dateshort')
|
# DATEFORMAT = xbmc.getRegion('dateshort')
|
||||||
# TIMEFORMAT = xbmc.getRegion('meridiem')
|
# TIMEFORMAT = xbmc.getRegion('meridiem')
|
||||||
|
@ -1415,85 +1410,53 @@ class API():
|
||||||
# else:
|
# else:
|
||||||
# localtime = time.strftime('%H:%M', date_time)
|
# localtime = time.strftime('%H:%M', date_time)
|
||||||
# return localtime + ' ' + localdate
|
# return localtime + ' ' + localdate
|
||||||
|
try:
|
||||||
DATEFORMAT = xbmc.getRegion('dateshort')
|
DATEFORMAT = xbmc.getRegion('dateshort')
|
||||||
TIMEFORMAT = xbmc.getRegion('meridiem')
|
TIMEFORMAT = xbmc.getRegion('meridiem')
|
||||||
date_time = time.localtime(float(stamp))
|
date_time = time.localtime(float(stamp))
|
||||||
localdate = time.strftime('%Y-%m-%dT%H:%M:%SZ', date_time)
|
localdate = time.strftime('%Y-%m-%dT%H:%M:%SZ', date_time)
|
||||||
|
except:
|
||||||
|
localdate = None
|
||||||
return localdate
|
return localdate
|
||||||
|
|
||||||
def getType(self):
|
def getType(self):
|
||||||
"""
|
"""
|
||||||
Returns the type of media, e.g. 'movie' or 'clip' for trailers
|
Returns the type of media, e.g. 'movie' or 'clip' for trailers
|
||||||
"""
|
"""
|
||||||
# XML
|
return self.item.attrib.get('type', None)
|
||||||
try:
|
|
||||||
item = self.item.attrib
|
|
||||||
# JSON
|
|
||||||
except AttributeError:
|
|
||||||
item = self.item
|
|
||||||
return item.get('type', '')
|
|
||||||
|
|
||||||
def getChecksum(self):
|
def getChecksum(self):
|
||||||
"""
|
"""
|
||||||
Can be used on both XML and JSON
|
|
||||||
Returns a string, not int
|
Returns a string, not int
|
||||||
"""
|
"""
|
||||||
item = self.item
|
|
||||||
# XML
|
|
||||||
try:
|
|
||||||
item = item[0].attrib
|
|
||||||
# JSON
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
pass
|
|
||||||
# Include a letter to prohibit saving as an int!
|
# Include a letter to prohibit saving as an int!
|
||||||
checksum = "K%s%s" % (self.getRatingKey(),
|
checksum = "K%s%s" % (self.getRatingKey(),
|
||||||
item.get('updatedAt', ''))
|
self.item.attrib.get('updatedAt', ''))
|
||||||
return checksum
|
return checksum
|
||||||
|
|
||||||
def getRatingKey(self):
|
def getRatingKey(self):
|
||||||
"""
|
"""
|
||||||
Can be used on both XML and JSON
|
|
||||||
Returns the Plex key such as '246922' as a string
|
Returns the Plex key such as '246922' as a string
|
||||||
"""
|
"""
|
||||||
# XML
|
return self.item.attrib.get('ratingKey', '')
|
||||||
try:
|
|
||||||
result = self.item.attrib
|
|
||||||
# JSON
|
|
||||||
except AttributeError:
|
|
||||||
item = self.item
|
|
||||||
return item['ratingKey']
|
|
||||||
|
|
||||||
def getKey(self):
|
def getKey(self):
|
||||||
"""
|
"""
|
||||||
Can be used on both XML and JSON
|
|
||||||
Returns the Plex key such as '/library/metadata/246922'
|
Returns the Plex key such as '/library/metadata/246922'
|
||||||
"""
|
"""
|
||||||
item = self.item
|
return self.item.attrib.get('key', None)
|
||||||
# XML
|
|
||||||
try:
|
|
||||||
item = item[0].attrib
|
|
||||||
# JSON
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
pass
|
|
||||||
key = item['key']
|
|
||||||
return str(key)
|
|
||||||
|
|
||||||
def getIndex(self):
|
def getIndex(self):
|
||||||
"""
|
"""
|
||||||
Returns the 'index' of an PMS XML reply. Depicts e.g. season number.
|
Returns the 'index' of an PMS XML reply. Depicts e.g. season number.
|
||||||
"""
|
"""
|
||||||
item = self.item[0].attrib
|
return self.item.attrib.get('index', None)
|
||||||
index = item['index']
|
|
||||||
return str(index)
|
|
||||||
|
|
||||||
def getDateCreated(self):
|
def getDateCreated(self):
|
||||||
"""
|
"""
|
||||||
Returns the date when this library item was created
|
Returns the date when this library item was created
|
||||||
"""
|
"""
|
||||||
item = self.item[0].attrib
|
return self.DateToKodi(self.item.attrib.get('addedAt', None))
|
||||||
dateadded = item['addedAt']
|
|
||||||
dateadded = self.convert_date(dateadded)
|
|
||||||
return dateadded
|
|
||||||
|
|
||||||
def getUserData(self):
|
def getUserData(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1508,7 +1471,7 @@ class API():
|
||||||
'Rating': rating
|
'Rating': rating
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
item = self.item
|
item = self.item.attrib
|
||||||
# Default
|
# Default
|
||||||
favorite = False
|
favorite = False
|
||||||
playcount = None
|
playcount = None
|
||||||
|
@ -1517,13 +1480,6 @@ class API():
|
||||||
resume = 0
|
resume = 0
|
||||||
rating = 0
|
rating = 0
|
||||||
|
|
||||||
# XML
|
|
||||||
try:
|
|
||||||
item = item[0].attrib
|
|
||||||
# JSON
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
playcount = int(item['viewCount'])
|
playcount = int(item['viewCount'])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -1533,8 +1489,7 @@ class API():
|
||||||
played = True
|
played = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lastPlayedDate = int(item['lastViewedAt'])
|
lastPlayedDate = self.DateToKodi(int(item['lastViewedAt']))
|
||||||
lastPlayedDate = self.convert_date(lastPlayedDate)
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
lastPlayedDate = None
|
lastPlayedDate = None
|
||||||
|
|
||||||
|
@ -1559,12 +1514,11 @@ class API():
|
||||||
'Producer': list
|
'Producer': list
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
item = self.item
|
|
||||||
director = []
|
director = []
|
||||||
writer = []
|
writer = []
|
||||||
cast = []
|
cast = []
|
||||||
producer = []
|
producer = []
|
||||||
for child in item[0]:
|
for child in self.item:
|
||||||
if child.tag == 'Director':
|
if child.tag == 'Director':
|
||||||
director.append(child.attrib['tag'])
|
director.append(child.attrib['tag'])
|
||||||
elif child.tag == 'Writer':
|
elif child.tag == 'Writer':
|
||||||
|
@ -1591,7 +1545,6 @@ class API():
|
||||||
('Role': xxx for cast/actors only, None if not found)
|
('Role': xxx for cast/actors only, None if not found)
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
item = self.item
|
|
||||||
people = []
|
people = []
|
||||||
# Key of library: Plex-identifier. Value represents the Kodi/emby side
|
# Key of library: Plex-identifier. Value represents the Kodi/emby side
|
||||||
people_of_interest = {
|
people_of_interest = {
|
||||||
|
@ -1600,7 +1553,7 @@ class API():
|
||||||
'Role': 'Actor',
|
'Role': 'Actor',
|
||||||
'Producer': 'Producer'
|
'Producer': 'Producer'
|
||||||
}
|
}
|
||||||
for child in item[0]:
|
for child in self.item:
|
||||||
if child.tag in people_of_interest.keys():
|
if child.tag in people_of_interest.keys():
|
||||||
name = child.attrib['tag']
|
name = child.attrib['tag']
|
||||||
name_id = child.attrib['id']
|
name_id = child.attrib['id']
|
||||||
|
@ -1630,9 +1583,8 @@ class API():
|
||||||
"""
|
"""
|
||||||
Returns a list of genres found. (Not a string)
|
Returns a list of genres found. (Not a string)
|
||||||
"""
|
"""
|
||||||
item = self.item
|
|
||||||
genre = []
|
genre = []
|
||||||
for child in item[0]:
|
for child in self.item:
|
||||||
if child.tag == 'Genre':
|
if child.tag == 'Genre':
|
||||||
genre.append(child.attrib['tag'])
|
genre.append(child.attrib['tag'])
|
||||||
return genre
|
return genre
|
||||||
|
@ -1643,8 +1595,7 @@ class API():
|
||||||
|
|
||||||
Return IMDB, e.g. "imdb://tt0903624?lang=en". Returns None if not found
|
Return IMDB, e.g. "imdb://tt0903624?lang=en". Returns None if not found
|
||||||
"""
|
"""
|
||||||
item = self.item
|
item = self.item.attrib
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
try:
|
||||||
item = item['guid']
|
item = item['guid']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -1660,99 +1611,50 @@ class API():
|
||||||
|
|
||||||
def getTitle(self):
|
def getTitle(self):
|
||||||
"""
|
"""
|
||||||
Returns an item's name/title or "Missing Title Name" for both XML and
|
Returns an item's name/title or "Missing Title Name".
|
||||||
JSON PMS replies
|
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
title, sorttitle
|
title, sorttitle
|
||||||
|
|
||||||
sorttitle = title, if no sorttitle is found
|
sorttitle = title, if no sorttitle is found
|
||||||
"""
|
"""
|
||||||
item = self.item
|
title = self.item.attrib.get('title', 'Missing Title Name')
|
||||||
|
sorttitle = self.item.attrib.get('titleSort', title)
|
||||||
# XML
|
|
||||||
try:
|
|
||||||
item = item[0].attrib
|
|
||||||
# JSON
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
title = item['title']
|
|
||||||
except:
|
|
||||||
title = 'Missing Title Name'
|
|
||||||
try:
|
|
||||||
sorttitle = item['titleSort']
|
|
||||||
except KeyError:
|
|
||||||
sorttitle = title
|
|
||||||
return title, sorttitle
|
return title, sorttitle
|
||||||
|
|
||||||
def getPlot(self):
|
def getPlot(self):
|
||||||
"""
|
"""
|
||||||
Returns the plot or None.
|
Returns the plot or None.
|
||||||
"""
|
"""
|
||||||
item = self.item
|
return self.item.attrib.get('summary', None)
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
|
||||||
plot = item['summary']
|
|
||||||
except:
|
|
||||||
plot = None
|
|
||||||
return plot
|
|
||||||
|
|
||||||
def getTagline(self):
|
def getTagline(self):
|
||||||
"""
|
"""
|
||||||
Returns a shorter tagline or None
|
Returns a shorter tagline or None
|
||||||
"""
|
"""
|
||||||
item = self.item
|
return self.item.attrib.get('tagline', None)
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
|
||||||
tagline = item['tagline']
|
|
||||||
except KeyError:
|
|
||||||
tagline = None
|
|
||||||
return tagline
|
|
||||||
|
|
||||||
def getAudienceRating(self):
|
def getAudienceRating(self):
|
||||||
"""
|
"""
|
||||||
Returns the audience rating or None
|
Returns the audience rating or None
|
||||||
"""
|
"""
|
||||||
item = self.item
|
return self.item.attrib.get('audienceRating', None)
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
|
||||||
rating = item['audienceRating']
|
|
||||||
except KeyError:
|
|
||||||
rating = None
|
|
||||||
return rating
|
|
||||||
|
|
||||||
def getYear(self):
|
def getYear(self):
|
||||||
"""
|
"""
|
||||||
Returns the production(?) year ("year") or None
|
Returns the production(?) year ("year") or None
|
||||||
"""
|
"""
|
||||||
item = self.item
|
return self.item.attrib.get('year', None)
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
|
||||||
year = item['year']
|
|
||||||
except KeyError:
|
|
||||||
year = None
|
|
||||||
return year
|
|
||||||
|
|
||||||
def getRuntime(self):
|
def getRuntime(self):
|
||||||
"""
|
"""
|
||||||
Resume point of time and runtime/totaltime in seconds, rounded to 6th
|
Resume point of time and runtime/totaltime in rounded to seconds.
|
||||||
decimal.
|
|
||||||
Time from Plex server is measured in milliseconds.
|
Time from Plex server is measured in milliseconds.
|
||||||
Kodi: seconds
|
Kodi: seconds
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
resume, runtime as floats. 0.0 if not found
|
resume, runtime as ints. 0 if not found
|
||||||
"""
|
"""
|
||||||
time_factor = PlexToKodiTimefactor()
|
item = self.item.attrib
|
||||||
|
|
||||||
# XML
|
|
||||||
try:
|
|
||||||
item = self.item[0].attrib
|
|
||||||
# JSON
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
runtime = float(item['duration'])
|
runtime = float(item['duration'])
|
||||||
|
@ -1763,30 +1665,16 @@ class API():
|
||||||
except KeyError:
|
except KeyError:
|
||||||
resume = 0.0
|
resume = 0.0
|
||||||
|
|
||||||
# Adjust the resume point by x seconds as chosen by the user in the
|
runtime = int(runtime * PlexToKodiTimefactor())
|
||||||
# settings
|
resume = int(resume * PlexToKodiTimefactor())
|
||||||
if resume:
|
|
||||||
# To avoid negative bookmark
|
|
||||||
if resume > self.jumpback:
|
|
||||||
resume = resume - self.jumpback
|
|
||||||
|
|
||||||
runtime = runtime * time_factor
|
|
||||||
resume = resume * time_factor
|
|
||||||
resume = round(resume, 6)
|
|
||||||
runtime = round(runtime, 6)
|
|
||||||
return resume, runtime
|
return resume, runtime
|
||||||
|
|
||||||
def getMpaa(self):
|
def getMpaa(self):
|
||||||
"""
|
"""
|
||||||
Get the content rating or None
|
Get the content rating or None
|
||||||
"""
|
"""
|
||||||
|
mpaa = self.item.attrib.get('contentRating', None)
|
||||||
# Convert more complex cases
|
# Convert more complex cases
|
||||||
item = self.item
|
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
|
||||||
mpaa = item['contentRating']
|
|
||||||
except KeyError:
|
|
||||||
mpaa = None
|
|
||||||
if mpaa in ("NR", "UR"):
|
if mpaa in ("NR", "UR"):
|
||||||
# Kodi seems to not like NR, but will accept Rated Not Rated
|
# Kodi seems to not like NR, but will accept Rated Not Rated
|
||||||
mpaa = "Rated Not Rated"
|
mpaa = "Rated Not Rated"
|
||||||
|
@ -1796,9 +1684,8 @@ class API():
|
||||||
"""
|
"""
|
||||||
Returns a list of all countries found in item.
|
Returns a list of all countries found in item.
|
||||||
"""
|
"""
|
||||||
item = self.item
|
|
||||||
country = []
|
country = []
|
||||||
for child in item[0]:
|
for child in self.item:
|
||||||
if child.tag == 'Country':
|
if child.tag == 'Country':
|
||||||
country.append(child.attrib['tag'])
|
country.append(child.attrib['tag'])
|
||||||
return country
|
return country
|
||||||
|
@ -1807,23 +1694,15 @@ class API():
|
||||||
"""
|
"""
|
||||||
Returns the "originallyAvailableAt" or None
|
Returns the "originallyAvailableAt" or None
|
||||||
"""
|
"""
|
||||||
item = self.item
|
return self.item.attrib.get('originallyAvailableAt', None)
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
|
||||||
premiere = item['originallyAvailableAt']
|
|
||||||
except:
|
|
||||||
premiere = None
|
|
||||||
return premiere
|
|
||||||
|
|
||||||
def getStudios(self):
|
def getStudios(self):
|
||||||
"""
|
"""
|
||||||
Returns a list with a single entry for the studio, or an empty list
|
Returns a list with a single entry for the studio, or an empty list
|
||||||
"""
|
"""
|
||||||
item = self.item
|
|
||||||
studio = []
|
studio = []
|
||||||
item = item[0].attrib
|
|
||||||
try:
|
try:
|
||||||
studio.append(self.getStudio(item['studio']))
|
studio.append(self.getStudio(self.item.attrib['studio']))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return studio
|
return studio
|
||||||
|
@ -1862,7 +1741,7 @@ class API():
|
||||||
Episode number, Plex: 'index'
|
Episode number, Plex: 'index'
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
item = self.item[0].attrib
|
item = self.item.attrib
|
||||||
key = item['grandparentRatingKey']
|
key = item['grandparentRatingKey']
|
||||||
title = item['grandparentTitle']
|
title = item['grandparentTitle']
|
||||||
season = item['parentIndex']
|
season = item['parentIndex']
|
||||||
|
@ -1891,13 +1770,7 @@ class API():
|
||||||
|
|
||||||
If not found, empty str is returned
|
If not found, empty str is returned
|
||||||
"""
|
"""
|
||||||
# XML:
|
return self.item.atrrib.get('playQueueItemID', '')
|
||||||
try:
|
|
||||||
item = self.item.attrib
|
|
||||||
# JSON
|
|
||||||
except AttributeError:
|
|
||||||
item = self.item
|
|
||||||
return item.get('playQueueItemID', '')
|
|
||||||
|
|
||||||
def getDataFromPartOrMedia(self, key):
|
def getDataFromPartOrMedia(self, key):
|
||||||
"""
|
"""
|
||||||
|
@ -1906,14 +1779,8 @@ class API():
|
||||||
|
|
||||||
If all fails, None is returned.
|
If all fails, None is returned.
|
||||||
"""
|
"""
|
||||||
# JSON
|
media = self.item.attrib
|
||||||
try:
|
part = self.item[self.part].attrib
|
||||||
media = self.item['_children'][0]
|
|
||||||
part = media['_children'][self.part]
|
|
||||||
# XML
|
|
||||||
except TypeError:
|
|
||||||
media = self.item[0].attrib
|
|
||||||
part = self.item[0][self.part].attrib
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
@ -1979,23 +1846,22 @@ class API():
|
||||||
'originallyAvailableAt':
|
'originallyAvailableAt':
|
||||||
'year':
|
'year':
|
||||||
"""
|
"""
|
||||||
extras = self.item[0].find('Extras')
|
|
||||||
elements = []
|
elements = []
|
||||||
if not extras:
|
for extra in self.item.find('Extras'):
|
||||||
return elements
|
|
||||||
for extra in extras:
|
|
||||||
# Trailer:
|
# Trailer:
|
||||||
key = extra.attrib['key']
|
key = extra.attrib.get('key', None)
|
||||||
title = extra.attrib['title']
|
title = extra.attrib.get('title', None)
|
||||||
thumb = extra.attrib['thumb']
|
thumb = extra.attrib.get('thumb', None)
|
||||||
duration = extra.attrib['duration']
|
duration = float(extra.attrib.get('duration', 0.0))
|
||||||
year = extra.attrib['year']
|
year = extra.attrib.get('year', None)
|
||||||
extraType = extra.attrib['extraType']
|
extraType = extra.attrib.get('extraType', None)
|
||||||
originallyAvailableAt = extra.attrib['originallyAvailableAt']
|
originallyAvailableAt = extra.attrib.get(
|
||||||
elements.append({'key': key,
|
'originallyAvailableAt', None)
|
||||||
|
elements.append(
|
||||||
|
{'key': key,
|
||||||
'title': title,
|
'title': title,
|
||||||
'thumb': thumb,
|
'thumb': thumb,
|
||||||
'duration': duration,
|
'duration': int(duration * PlexToKodiTimefactor()),
|
||||||
'extraType': extraType,
|
'extraType': extraType,
|
||||||
'originallyAvailableAt': originallyAvailableAt,
|
'originallyAvailableAt': originallyAvailableAt,
|
||||||
'year': year})
|
'year': year})
|
||||||
|
@ -2014,24 +1880,20 @@ class API():
|
||||||
'subtitle': list of subtitle languages (or "Unknown")
|
'subtitle': list of subtitle languages (or "Unknown")
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
item = self.item
|
|
||||||
videotracks = []
|
videotracks = []
|
||||||
audiotracks = []
|
audiotracks = []
|
||||||
subtitlelanguages = []
|
subtitlelanguages = []
|
||||||
aspectratio = None
|
# Sometimes, aspectratio is on the "toplevel"
|
||||||
try:
|
aspectratio = self.item[0].attrib.get('aspectRatio', None)
|
||||||
aspectratio = item[0][0].attrib['aspectRatio']
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
# TODO: what if several Media tags exist?!?
|
# TODO: what if several Media tags exist?!?
|
||||||
# Loop over parts
|
# Loop over parts
|
||||||
for child in item[0][0]:
|
for child in self.item[0]:
|
||||||
container = child.attrib['container'].lower()
|
container = child.attrib.get('container', None)
|
||||||
# Loop over Streams
|
# Loop over Streams
|
||||||
for grandchild in child:
|
for grandchild in child:
|
||||||
mediaStream = grandchild.attrib
|
mediaStream = grandchild.attrib
|
||||||
type = int(mediaStream['streamType'])
|
mediaType = int(mediaStream.get('streamType', 999))
|
||||||
if type == 1: # Video streams
|
if mediaType == 1: # Video streams
|
||||||
videotrack = {}
|
videotrack = {}
|
||||||
videotrack['codec'] = mediaStream['codec'].lower()
|
videotrack['codec'] = mediaStream['codec'].lower()
|
||||||
if "msmpeg4" in videotrack['codec']:
|
if "msmpeg4" in videotrack['codec']:
|
||||||
|
@ -2043,21 +1905,17 @@ class API():
|
||||||
elif "h264" in videotrack['codec']:
|
elif "h264" in videotrack['codec']:
|
||||||
if container in ("mp4", "mov", "m4v"):
|
if container in ("mp4", "mov", "m4v"):
|
||||||
videotrack['codec'] = "avc1"
|
videotrack['codec'] = "avc1"
|
||||||
videotrack['height'] = mediaStream.get('height')
|
videotrack['height'] = mediaStream.get('height', None)
|
||||||
videotrack['width'] = mediaStream.get('width')
|
videotrack['width'] = mediaStream.get('width', None)
|
||||||
# TODO: 3d Movies?!?
|
# TODO: 3d Movies?!?
|
||||||
# videotrack['Video3DFormat'] = item.get('Video3DFormat')
|
# videotrack['Video3DFormat'] = item.get('Video3DFormat')
|
||||||
try:
|
aspectratio = mediaStream.get('aspectRatio', aspectratio)
|
||||||
aspectratio = mediaStream['aspectRatio']
|
|
||||||
except KeyError:
|
|
||||||
if not aspectratio:
|
|
||||||
aspectratio = round(float(videotrack['width'] / videotrack['height']), 6)
|
|
||||||
videotrack['aspect'] = aspectratio
|
videotrack['aspect'] = aspectratio
|
||||||
# TODO: Video 3d format
|
# TODO: Video 3d format
|
||||||
videotrack['video3DFormat'] = None
|
videotrack['video3DFormat'] = None
|
||||||
videotracks.append(videotrack)
|
videotracks.append(videotrack)
|
||||||
|
|
||||||
elif type == 2: # Audio streams
|
elif mediaType == 2: # Audio streams
|
||||||
audiotrack = {}
|
audiotrack = {}
|
||||||
audiotrack['codec'] = mediaStream['codec'].lower()
|
audiotrack['codec'] = mediaStream['codec'].lower()
|
||||||
profile = mediaStream['codecID'].lower()
|
profile = mediaStream['codecID'].lower()
|
||||||
|
@ -2070,17 +1928,16 @@ class API():
|
||||||
audiotrack['language'] = 'unknown'
|
audiotrack['language'] = 'unknown'
|
||||||
audiotracks.append(audiotrack)
|
audiotracks.append(audiotrack)
|
||||||
|
|
||||||
elif type == 3: # Subtitle streams
|
elif mediaType == 3: # Subtitle streams
|
||||||
try:
|
try:
|
||||||
subtitlelanguages.append(mediaStream['language'])
|
subtitlelanguages.append(mediaStream['language'])
|
||||||
except:
|
except:
|
||||||
subtitlelanguages.append("Unknown")
|
subtitlelanguages.append("Unknown")
|
||||||
media = {
|
return {
|
||||||
'video': videotracks,
|
'video': videotracks,
|
||||||
'audio': audiotracks,
|
'audio': audiotracks,
|
||||||
'subtitle': subtitlelanguages
|
'subtitle': subtitlelanguages
|
||||||
}
|
}
|
||||||
return media
|
|
||||||
|
|
||||||
def getAllArtwork(self, parentInfo=False):
|
def getAllArtwork(self, parentInfo=False):
|
||||||
"""
|
"""
|
||||||
|
@ -2097,15 +1954,7 @@ class API():
|
||||||
'Backdrop': [] Plex key: "art". Only 1 pix
|
'Backdrop': [] Plex key: "art". Only 1 pix
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
server = self.server
|
item = self.item.attrib
|
||||||
item = self.item
|
|
||||||
|
|
||||||
# XML
|
|
||||||
try:
|
|
||||||
item = item[0].attrib
|
|
||||||
# JSON
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
maxHeight = 10000
|
maxHeight = 10000
|
||||||
maxWidth = 10000
|
maxWidth = 10000
|
||||||
|
@ -2130,7 +1979,7 @@ class API():
|
||||||
# Get background artwork URL
|
# Get background artwork URL
|
||||||
try:
|
try:
|
||||||
background = item['art']
|
background = item['art']
|
||||||
background = "%s%s" % (server, background)
|
background = "%s%s" % (self.server, background)
|
||||||
background = self.addPlexCredentialsToUrl(background)
|
background = self.addPlexCredentialsToUrl(background)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
background = ""
|
background = ""
|
||||||
|
@ -2138,7 +1987,7 @@ class API():
|
||||||
# Get primary "thumb" pictures:
|
# Get primary "thumb" pictures:
|
||||||
try:
|
try:
|
||||||
primary = item['thumb']
|
primary = item['thumb']
|
||||||
primary = "%s%s" % (server, primary)
|
primary = "%s%s" % (self.server, primary)
|
||||||
primary = self.addPlexCredentialsToUrl(primary)
|
primary = self.addPlexCredentialsToUrl(primary)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
primary = ""
|
primary = ""
|
||||||
|
@ -2160,7 +2009,7 @@ class API():
|
||||||
artwork = (
|
artwork = (
|
||||||
"%s/emby/Items/%s/Images/Backdrop/%s?"
|
"%s/emby/Items/%s/Images/Backdrop/%s?"
|
||||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||||
% (server, parentId, backdropIndex,
|
% (self.server, parentId, backdropIndex,
|
||||||
maxWidth, maxHeight, parentbackdroptag, customquery))
|
maxWidth, maxHeight, parentbackdroptag, customquery))
|
||||||
allartworks['Backdrop'].append(artwork)
|
allartworks['Backdrop'].append(artwork)
|
||||||
backdropIndex += 1
|
backdropIndex += 1
|
||||||
|
@ -2178,7 +2027,7 @@ class API():
|
||||||
artwork = (
|
artwork = (
|
||||||
"%s/emby/Items/%s/Images/%s/0?"
|
"%s/emby/Items/%s/Images/%s/0?"
|
||||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||||
% (server, parentId, parentart,
|
% (self.server, parentId, parentart,
|
||||||
maxWidth, maxHeight, parentTag, customquery))
|
maxWidth, maxHeight, parentTag, customquery))
|
||||||
allartworks[parentart] = artwork
|
allartworks[parentart] = artwork
|
||||||
|
|
||||||
|
@ -2192,11 +2041,12 @@ class API():
|
||||||
artwork = (
|
artwork = (
|
||||||
"%s/emby/Items/%s/Images/Primary/0?"
|
"%s/emby/Items/%s/Images/Primary/0?"
|
||||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||||
% (server, parentId, maxWidth, maxHeight, parentTag, customquery))
|
% (self.server, parentId, maxWidth, maxHeight, parentTag, customquery))
|
||||||
allartworks['Primary'] = artwork
|
allartworks['Primary'] = artwork
|
||||||
return allartworks
|
return allartworks
|
||||||
|
|
||||||
def getTranscodeVideoPath(self, action, quality={}, subtitle={}, audioboost=None, options={}):
|
def getTranscodeVideoPath(self, action, quality={}, subtitle={},
|
||||||
|
audioboost=None, options={}):
|
||||||
"""
|
"""
|
||||||
Transcode Video support; returns the URL to get a media started
|
Transcode Video support; returns the URL to get a media started
|
||||||
|
|
||||||
|
@ -2329,12 +2179,12 @@ class API():
|
||||||
mapping = {}
|
mapping = {}
|
||||||
|
|
||||||
item = self.item
|
item = self.item
|
||||||
itemid = self.getRatingKey()
|
|
||||||
try:
|
try:
|
||||||
mediastreams = item[0][0][0]
|
mediastreams = item[0][self.part]
|
||||||
except (TypeError, KeyError, IndexError):
|
except (TypeError, KeyError, IndexError):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
itemid = self.getRatingKey()
|
||||||
kodiindex = 0
|
kodiindex = 0
|
||||||
for stream in mediastreams:
|
for stream in mediastreams:
|
||||||
# index = stream['Index']
|
# index = stream['Index']
|
||||||
|
@ -2394,9 +2244,3 @@ class API():
|
||||||
if not xml:
|
if not xml:
|
||||||
self.logMsg("Error retrieving metadata for %s" % url, 1)
|
self.logMsg("Error retrieving metadata for %s" % url, 1)
|
||||||
return xml
|
return xml
|
||||||
|
|
||||||
def GetParts(self):
|
|
||||||
"""
|
|
||||||
Returns the parts of the specified video child in the XML response
|
|
||||||
"""
|
|
||||||
return self.item[0][0]
|
|
||||||
|
|
|
@ -115,16 +115,15 @@ def GetPlayQueue(playQueueID):
|
||||||
return xml
|
return xml
|
||||||
|
|
||||||
|
|
||||||
def GetPlexMetadata(key, JSON=True):
|
def GetPlexMetadata(key):
|
||||||
"""
|
"""
|
||||||
Returns raw API metadata for key as an etree XML.
|
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
|
Returns None if something went wrong
|
||||||
"""
|
"""
|
||||||
xml = ''
|
|
||||||
key = str(key)
|
key = str(key)
|
||||||
if '/library/metadata/' in key:
|
if '/library/metadata/' in key:
|
||||||
url = "{server}" + key
|
url = "{server}" + key
|
||||||
|
@ -133,82 +132,72 @@ def GetPlexMetadata(key, JSON=True):
|
||||||
arguments = {
|
arguments = {
|
||||||
'checkFiles': 1, # No idea
|
'checkFiles': 1, # No idea
|
||||||
'includeExtras': 1, # Trailers and Extras => Extras
|
'includeExtras': 1, # Trailers and Extras => Extras
|
||||||
'includeRelated': 1, # Similar movies => Video -> Related
|
# 'includeRelated': 1, # Similar movies => Video -> Related
|
||||||
'includeRelatedCount': 5,
|
# 'includeRelatedCount': 5,
|
||||||
'includeOnDeck': 1,
|
# 'includeOnDeck': 1,
|
||||||
'includeChapters': 1,
|
'includeChapters': 1,
|
||||||
'includePopularLeaves': 1,
|
'includePopularLeaves': 1,
|
||||||
'includeConcerts': 1
|
'includeConcerts': 1
|
||||||
}
|
}
|
||||||
url = url + '?' + urlencode(arguments)
|
url = url + '?' + urlencode(arguments)
|
||||||
if not JSON:
|
xml = downloadutils.DownloadUtils().downloadUrl(url)
|
||||||
headerOptions = {'Accept': 'application/xml'}
|
|
||||||
else:
|
|
||||||
headerOptions = {}
|
|
||||||
xml = downloadutils.DownloadUtils().downloadUrl(url, headerOptions=headerOptions)
|
|
||||||
# Did we receive a valid XML?
|
# Did we receive a valid XML?
|
||||||
try:
|
try:
|
||||||
xml.tag
|
xml.attrib
|
||||||
# Nope we did not receive a valid XML
|
# Nope we did not receive a valid XML
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logMsg(title, "Error retrieving metadata for %s" % url, -1)
|
logMsg(title, "Error retrieving metadata for %s" % url, -1)
|
||||||
xml = ''
|
xml = None
|
||||||
return xml
|
return xml
|
||||||
|
|
||||||
|
|
||||||
def GetAllPlexChildren(key):
|
def GetAllPlexChildren(key):
|
||||||
"""
|
"""
|
||||||
Returns a list (raw JSON API dump) of all Plex children for the key.
|
Returns a list (raw xml API dump) of all Plex children for the key.
|
||||||
(e.g. /library/metadata/194853/children pointing to a season)
|
(e.g. /library/metadata/194853/children pointing to a season)
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
key Key to a Plex item, e.g. 12345
|
key Key to a Plex item, e.g. 12345
|
||||||
"""
|
"""
|
||||||
result = []
|
xml = downloadutils.DownloadUtils().downloadUrl(
|
||||||
url = "{server}/library/metadata/%s/children" % key
|
"{server}/library/metadata/%s/children" % key)
|
||||||
jsondata = downloadutils.DownloadUtils().downloadUrl(url)
|
|
||||||
try:
|
try:
|
||||||
result = jsondata['_children']
|
xml.attrib
|
||||||
except KeyError:
|
except AttributeError:
|
||||||
logMsg(
|
logMsg(
|
||||||
title, "Error retrieving all children for Plex item %s" % key, -1)
|
title, "Error retrieving all children for Plex item %s" % key, -1)
|
||||||
pass
|
xml = None
|
||||||
return result
|
return xml
|
||||||
|
|
||||||
|
|
||||||
def GetPlexSectionResults(viewId, headerOptions={}):
|
def GetPlexSectionResults(viewId, headerOptions={}):
|
||||||
"""
|
"""
|
||||||
Returns a list (raw JSON or XML API dump) of all Plex items in the Plex
|
Returns a list (XML API dump) of all Plex items in the Plex
|
||||||
section with key = viewId.
|
section with key = viewId.
|
||||||
|
|
||||||
|
Returns None if something went wrong
|
||||||
"""
|
"""
|
||||||
result = []
|
result = []
|
||||||
url = "{server}/library/sections/%s/all" % viewId
|
url = "{server}/library/sections/%s/all" % viewId
|
||||||
jsondata = downloadutils.DownloadUtils().downloadUrl(url, headerOptions=headerOptions)
|
result = downloadutils.DownloadUtils().downloadUrl(
|
||||||
|
url, headerOptions=headerOptions)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = jsondata['_children']
|
result.tag
|
||||||
except TypeError:
|
|
||||||
# Maybe we received an XML, check for that with tag attribute
|
|
||||||
try:
|
|
||||||
jsondata.tag
|
|
||||||
result = jsondata
|
|
||||||
# Nope, not an XML, abort
|
# Nope, not an XML, abort
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logMsg(title,
|
logMsg(title,
|
||||||
"Error retrieving all items for Plex section %s"
|
"Error retrieving all items for Plex section %s"
|
||||||
% viewId, -1)
|
% viewId, -1)
|
||||||
return result
|
result = None
|
||||||
except KeyError:
|
|
||||||
logMsg(title,
|
|
||||||
"Error retrieving all items for Plex section %s"
|
|
||||||
% viewId, -1)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None,
|
def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None,
|
||||||
headerOptions={}):
|
headerOptions={}):
|
||||||
"""
|
"""
|
||||||
Returns a list (raw JSON or XML API dump) of all Plex subitems for the
|
Returns a list (raw XML API dump) of all Plex subitems for the key.
|
||||||
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:
|
||||||
|
@ -226,7 +215,6 @@ def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None,
|
||||||
Relevant "master time": PMS server. I guess this COULD lead to problems,
|
Relevant "master time": PMS server. I guess this COULD lead to problems,
|
||||||
e.g. when server and client are in different time zones.
|
e.g. when server and client are in different time zones.
|
||||||
"""
|
"""
|
||||||
result = []
|
|
||||||
args = []
|
args = []
|
||||||
url = "{server}/library/sections/%s/allLeaves?" % viewId
|
url = "{server}/library/sections/%s/allLeaves?" % viewId
|
||||||
if lastViewedAt:
|
if lastViewedAt:
|
||||||
|
@ -234,24 +222,18 @@ def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None,
|
||||||
if updatedAt:
|
if updatedAt:
|
||||||
args.append('updatedAt>=%s' % updatedAt)
|
args.append('updatedAt>=%s' % updatedAt)
|
||||||
args = '&'.join(args)
|
args = '&'.join(args)
|
||||||
jsondata = downloadutils.DownloadUtils().downloadUrl(
|
xml = downloadutils.DownloadUtils().downloadUrl(
|
||||||
url+args, headerOptions=headerOptions)
|
url+args, headerOptions=headerOptions)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = jsondata['_children']
|
xml.attrib
|
||||||
except TypeError:
|
|
||||||
# Maybe we received an XML, check for that with tag attribute
|
|
||||||
try:
|
|
||||||
jsondata.tag
|
|
||||||
result = jsondata
|
|
||||||
# Nope, not an XML, abort
|
# Nope, not an XML, abort
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logMsg(title,
|
logMsg(title,
|
||||||
"Error retrieving all leaves for Plex section %s"
|
"Error retrieving all leaves for Plex section %s"
|
||||||
% viewId, -1)
|
% viewId, -1)
|
||||||
return result
|
xml = None
|
||||||
except KeyError:
|
return xml
|
||||||
logMsg("Error retrieving all leaves for Plex viewId %s" % viewId, -1)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def GetPlexCollections(mediatype):
|
def GetPlexCollections(mediatype):
|
||||||
|
|
|
@ -180,9 +180,9 @@ class DownloadUtils():
|
||||||
plx = PlexAPI.PlexAPI()
|
plx = PlexAPI.PlexAPI()
|
||||||
if authenticate:
|
if authenticate:
|
||||||
options['X-Plex-Token'] = self.token
|
options['X-Plex-Token'] = self.token
|
||||||
header = plx.getXArgsDeviceInfo(options=options, JSON=True)
|
header = plx.getXArgsDeviceInfo(options=options)
|
||||||
else:
|
else:
|
||||||
header = plx.getXArgsDeviceInfo(options=options, JSON=True)
|
header = plx.getXArgsDeviceInfo(options=options)
|
||||||
return header
|
return header
|
||||||
|
|
||||||
def downloadUrl(self, url, postBody=None, type="GET", parameters=None, authenticate=True, headerOptions={}):
|
def downloadUrl(self, url, postBody=None, type="GET", parameters=None, authenticate=True, headerOptions={}):
|
||||||
|
@ -308,20 +308,21 @@ class DownloadUtils():
|
||||||
|
|
||||||
elif r.status_code == requests.codes.ok:
|
elif r.status_code == requests.codes.ok:
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Allow for xml responses
|
||||||
|
r = etree.fromstring(r.content)
|
||||||
|
self.logMsg("====== 200 Success ======", 2)
|
||||||
|
self.logMsg("Received an XML response for: %s" % url, 2)
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
except:
|
||||||
try:
|
try:
|
||||||
# UNICODE - JSON object
|
# UNICODE - JSON object
|
||||||
r = r.json()
|
r = r.json()
|
||||||
self.logMsg("====== 200 Success ======", 2)
|
self.logMsg("====== 200 Success ======", 2)
|
||||||
self.logMsg("Response: %s" % r, 2)
|
self.logMsg("Response: %s" % r, 2)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
except:
|
|
||||||
# Allow for xml responses
|
|
||||||
try:
|
|
||||||
r = etree.fromstring(r.content)
|
|
||||||
self.logMsg("====== 200 Success ======", 2)
|
|
||||||
self.logMsg("Received an XML response for: %s" % url, 2)
|
|
||||||
return r
|
|
||||||
except:
|
except:
|
||||||
try:
|
try:
|
||||||
if r.text == '' and r.status_code == 200:
|
if r.text == '' and r.status_code == 200:
|
||||||
|
|
|
@ -105,7 +105,7 @@ def PassPlaylist(xml, resume=0):
|
||||||
def doPlayback(itemid, dbid):
|
def doPlayback(itemid, dbid):
|
||||||
utils.logMsg(title, "doPlayback called with %s %s"
|
utils.logMsg(title, "doPlayback called with %s %s"
|
||||||
% (itemid, dbid), 1)
|
% (itemid, dbid), 1)
|
||||||
item = PlexFunctions.GetPlexMetadata(itemid, JSON=True)
|
item = PlexFunctions.GetPlexMetadata(itemid)
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
# If current playlist is NOT empty, we only need to update the item url
|
# If current playlist is NOT empty, we only need to update the item url
|
||||||
if playlist.size() != 0:
|
if playlist.size() != 0:
|
||||||
|
|
|
@ -317,12 +317,15 @@ class Movies(Items):
|
||||||
userdata = API.getUserData()
|
userdata = API.getUserData()
|
||||||
playcount = userdata['PlayCount']
|
playcount = userdata['PlayCount']
|
||||||
dateplayed = userdata['LastPlayedDate']
|
dateplayed = userdata['LastPlayedDate']
|
||||||
|
resume = userdata['Resume']
|
||||||
|
runtime = userdata['Runtime']
|
||||||
|
|
||||||
# item details
|
# item details
|
||||||
people = API.getPeople()
|
people = API.getPeople()
|
||||||
writer = API.joinList(people['Writer'])
|
writer = API.joinList(people['Writer'])
|
||||||
director = API.joinList(people['Director'])
|
director = API.joinList(people['Director'])
|
||||||
genres = API.getGenres()
|
genres = API.getGenres()
|
||||||
|
genre = API.joinList(genres)
|
||||||
title, sorttitle = API.getTitle()
|
title, sorttitle = API.getTitle()
|
||||||
plot = API.getPlot()
|
plot = API.getPlot()
|
||||||
shortplot = None
|
shortplot = None
|
||||||
|
@ -331,10 +334,8 @@ class Movies(Items):
|
||||||
|
|
||||||
rating = API.getAudienceRating()
|
rating = API.getAudienceRating()
|
||||||
year = API.getYear()
|
year = API.getYear()
|
||||||
imdb = API.getProvider('Imdb')
|
imdb = API.getProvider()
|
||||||
resume, runtime = API.getRuntime()
|
|
||||||
mpaa = API.getMpaa()
|
mpaa = API.getMpaa()
|
||||||
genre = API.joinList(genres)
|
|
||||||
countries = API.getCountry()
|
countries = API.getCountry()
|
||||||
country = API.joinList(countries)
|
country = API.joinList(countries)
|
||||||
studios = API.getStudios()
|
studios = API.getStudios()
|
||||||
|
@ -350,8 +351,8 @@ class Movies(Items):
|
||||||
for extra in extras:
|
for extra in extras:
|
||||||
# Only get 1st trailer element
|
# Only get 1st trailer element
|
||||||
if extra['extraType'] == '1':
|
if extra['extraType'] == '1':
|
||||||
trailer = extra['key']
|
trailer = ("plugin://plugin.video.plexkodiconnect/trailer/?"
|
||||||
trailer = "plugin://plugin.video.plexkodiconnect/trailer/?id=%s&mode=play" % trailer
|
"id=%s&mode=play") % extra['key']
|
||||||
self.logMsg("Trailer for %s: %s" % (itemid, trailer), 2)
|
self.logMsg("Trailer for %s: %s" % (itemid, trailer), 2)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
@ -63,10 +63,8 @@ class ThreadedGetMetadata(threading.Thread):
|
||||||
continue
|
continue
|
||||||
# Download Metadata
|
# Download Metadata
|
||||||
plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId'])
|
plexXML = PlexFunctions.GetPlexMetadata(updateItem['itemId'])
|
||||||
try:
|
if not plexXML:
|
||||||
plexXML.tag
|
# Did not receive a valid XML - skip that item for now
|
||||||
except:
|
|
||||||
# Did not receive a valid XML - skip that one for now
|
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -129,7 +127,9 @@ class ThreadedProcessMetadata(threading.Thread):
|
||||||
title = updateItem['title']
|
title = updateItem['title']
|
||||||
itemSubFkt = getattr(item, method)
|
itemSubFkt = getattr(item, method)
|
||||||
with lock:
|
with lock:
|
||||||
itemSubFkt(plexitem,
|
# Get the one child entry in the xml and process
|
||||||
|
for child in plexitem:
|
||||||
|
itemSubFkt(child,
|
||||||
viewtag=viewName,
|
viewtag=viewName,
|
||||||
viewid=viewId)
|
viewid=viewId)
|
||||||
# Keep track of where we are at
|
# Keep track of where we are at
|
||||||
|
@ -303,7 +303,7 @@ class LibrarySync(threading.Thread):
|
||||||
if not items:
|
if not items:
|
||||||
continue
|
continue
|
||||||
# Get one itemtype, because they're the same in the PMS section
|
# Get one itemtype, because they're the same in the PMS section
|
||||||
plexType = items[0]['type']
|
plexType = items[0].attrib['type']
|
||||||
# Populate self.updatelist
|
# Populate self.updatelist
|
||||||
self.GetUpdatelist(items,
|
self.GetUpdatelist(items,
|
||||||
PlexFunctions.GetItemClassFromType(plexType),
|
PlexFunctions.GetItemClassFromType(plexType),
|
||||||
|
@ -456,7 +456,10 @@ class LibrarySync(threading.Thread):
|
||||||
vnodes = self.vnodes
|
vnodes = self.vnodes
|
||||||
|
|
||||||
# Get views
|
# Get views
|
||||||
result = doUtils.downloadUrl("{server}/library/sections")['_children']
|
result = doUtils.downloadUrl("{server}/library/sections")
|
||||||
|
if not result:
|
||||||
|
self.logMsg("Error download PMS views, abort maintainViews", -1)
|
||||||
|
return False
|
||||||
|
|
||||||
# total nodes for window properties
|
# total nodes for window properties
|
||||||
vnodes.clearProperties()
|
vnodes.clearProperties()
|
||||||
|
@ -467,7 +470,8 @@ class LibrarySync(threading.Thread):
|
||||||
'movie',
|
'movie',
|
||||||
'show'
|
'show'
|
||||||
]
|
]
|
||||||
for folder in result:
|
for folderItem in result:
|
||||||
|
folder = folderItem.attrib
|
||||||
mediatype = folder['type']
|
mediatype = folder['type']
|
||||||
if mediatype in mediatypes:
|
if mediatype in mediatypes:
|
||||||
folderid = folder['key']
|
folderid = folder['key']
|
||||||
|
@ -567,13 +571,12 @@ class LibrarySync(threading.Thread):
|
||||||
embyconn.close()
|
embyconn.close()
|
||||||
kodiconn.close()
|
kodiconn.close()
|
||||||
|
|
||||||
def GetUpdatelist(self, elementList, itemType, method, viewName, viewId):
|
def GetUpdatelist(self, xml, itemType, method, viewName, viewId):
|
||||||
"""
|
"""
|
||||||
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. list of '_children'
|
xml: PMS answer for section items
|
||||||
movie elements as received from PMS
|
|
||||||
itemType: 'Movies', 'TVShows', ...
|
itemType: 'Movies', 'TVShows', ...
|
||||||
method: Method name to be called with this itemtype
|
method: Method name to be called with this itemtype
|
||||||
see itemtypes.py
|
see itemtypes.py
|
||||||
|
@ -596,12 +599,10 @@ class LibrarySync(threading.Thread):
|
||||||
"""
|
"""
|
||||||
if self.compare:
|
if self.compare:
|
||||||
# Manual sync
|
# Manual sync
|
||||||
for item in elementList:
|
for item in xml:
|
||||||
# Skipping XML item 'title=All episodes' without a 'ratingKey'
|
# Skipping items 'title=All episodes' without a 'ratingKey'
|
||||||
if not item.get('ratingKey', False):
|
if not item.attrib.get('ratingKey', False):
|
||||||
continue
|
continue
|
||||||
if self.threadStopped():
|
|
||||||
return False
|
|
||||||
API = PlexAPI.API(item)
|
API = PlexAPI.API(item)
|
||||||
plex_checksum = API.getChecksum()
|
plex_checksum = API.getChecksum()
|
||||||
itemId = API.getRatingKey()
|
itemId = API.getRatingKey()
|
||||||
|
@ -619,12 +620,10 @@ class LibrarySync(threading.Thread):
|
||||||
'title': title})
|
'title': title})
|
||||||
else:
|
else:
|
||||||
# Initial or repair sync: get all Plex movies
|
# Initial or repair sync: get all Plex movies
|
||||||
for item in elementList:
|
for item in xml:
|
||||||
# Only look at valid items = Plex library items
|
# Only look at valid items = Plex library items
|
||||||
if not item.get('ratingKey', False):
|
if not item.attrib.get('ratingKey', False):
|
||||||
continue
|
continue
|
||||||
if self.threadStopped():
|
|
||||||
return False
|
|
||||||
API = PlexAPI.API(item)
|
API = PlexAPI.API(item)
|
||||||
itemId = API.getRatingKey()
|
itemId = API.getRatingKey()
|
||||||
title, sorttitle = API.getTitle()
|
title, sorttitle = API.getTitle()
|
||||||
|
@ -679,7 +678,7 @@ class LibrarySync(threading.Thread):
|
||||||
thread.setDaemon(True)
|
thread.setDaemon(True)
|
||||||
thread.start()
|
thread.start()
|
||||||
threads.append(thread)
|
threads.append(thread)
|
||||||
self.logMsg("Download threads spawned", 1)
|
self.logMsg("%s download threads spawned" % len(threads), 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,
|
||||||
|
@ -758,6 +757,9 @@ class LibrarySync(threading.Thread):
|
||||||
viewId = view['id']
|
viewId = view['id']
|
||||||
viewName = view['name']
|
viewName = view['name']
|
||||||
all_plexmovies = PlexFunctions.GetPlexSectionResults(viewId)
|
all_plexmovies = PlexFunctions.GetPlexSectionResults(viewId)
|
||||||
|
if not all_plexmovies:
|
||||||
|
self.logMsg("Couldnt get section items, aborting for view.", 1)
|
||||||
|
continue
|
||||||
# Populate self.updatelist and self.allPlexElementsId
|
# Populate self.updatelist and self.allPlexElementsId
|
||||||
self.GetUpdatelist(all_plexmovies,
|
self.GetUpdatelist(all_plexmovies,
|
||||||
itemType,
|
itemType,
|
||||||
|
@ -768,6 +770,8 @@ class LibrarySync(threading.Thread):
|
||||||
self.logMsg("Processed view %s with ID %s" % (viewName, viewId), 1)
|
self.logMsg("Processed view %s with ID %s" % (viewName, viewId), 1)
|
||||||
# Update viewstate
|
# Update viewstate
|
||||||
for view in views:
|
for view in views:
|
||||||
|
if self.threadStopped():
|
||||||
|
return False
|
||||||
self.PlexUpdateWatched(view['id'], itemType)
|
self.PlexUpdateWatched(view['id'], itemType)
|
||||||
|
|
||||||
##### PROCESS DELETES #####
|
##### PROCESS DELETES #####
|
||||||
|
@ -892,6 +896,10 @@ class LibrarySync(threading.Thread):
|
||||||
viewId = view['id']
|
viewId = view['id']
|
||||||
viewName = view['name']
|
viewName = view['name']
|
||||||
allPlexTvShows = PlexFunctions.GetPlexSectionResults(viewId)
|
allPlexTvShows = PlexFunctions.GetPlexSectionResults(viewId)
|
||||||
|
if not allPlexTvShows:
|
||||||
|
self.logMsg(
|
||||||
|
"Error downloading show view xml for view %s" % viewId, -1)
|
||||||
|
continue
|
||||||
# Populate self.updatelist and self.allPlexElementsId
|
# Populate self.updatelist and self.allPlexElementsId
|
||||||
self.GetUpdatelist(allPlexTvShows,
|
self.GetUpdatelist(allPlexTvShows,
|
||||||
itemType,
|
itemType,
|
||||||
|
@ -910,6 +918,10 @@ class LibrarySync(threading.Thread):
|
||||||
return False
|
return False
|
||||||
# Grab all seasons to tvshow from PMS
|
# Grab all seasons to tvshow from PMS
|
||||||
seasons = PlexFunctions.GetAllPlexChildren(tvShowId)
|
seasons = PlexFunctions.GetAllPlexChildren(tvShowId)
|
||||||
|
if not seasons:
|
||||||
|
self.logMsg(
|
||||||
|
"Error downloading season xml for show %s" % tvShowId, -1)
|
||||||
|
continue
|
||||||
# Populate self.updatelist and self.allPlexElementsId
|
# Populate self.updatelist and self.allPlexElementsId
|
||||||
self.GetUpdatelist(seasons,
|
self.GetUpdatelist(seasons,
|
||||||
itemType,
|
itemType,
|
||||||
|
@ -926,6 +938,11 @@ class LibrarySync(threading.Thread):
|
||||||
return False
|
return False
|
||||||
# Grab all episodes to tvshow from PMS
|
# Grab all episodes to tvshow from PMS
|
||||||
episodes = PlexFunctions.GetAllPlexLeaves(view['id'])
|
episodes = PlexFunctions.GetAllPlexLeaves(view['id'])
|
||||||
|
if not episodes:
|
||||||
|
self.logMsg(
|
||||||
|
"Error downloading episod xml for view %s"
|
||||||
|
% view.get('name'), -1)
|
||||||
|
continue
|
||||||
# Populate self.updatelist and self.allPlexElementsId
|
# Populate self.updatelist and self.allPlexElementsId
|
||||||
self.GetUpdatelist(episodes,
|
self.GetUpdatelist(episodes,
|
||||||
itemType,
|
itemType,
|
||||||
|
|
Loading…
Reference in a new issue