@@ -45,6 +45,7 @@ class Plex(object):
def __init__(self):
self.logger = logging.getLogger('modules.plex')
self.headers = None
self._commandId = 0

htpc.MODULES.append({
'name': 'Plex',
@@ -93,14 +94,11 @@ def ping(self, plex_host='', plex_port='', plex_ssl='', **kwargs):
self.logger.debug('Testing Plex connectivity')
ssl = 's' if plex_ssl == 'on' else ''
url = 'http%s://%s:%s' % (ssl, striphttp(plex_host), plex_port)
print url
self.logger.debug('Trying to contact Plex via %s' % url)
request = loads(urlopen(Request(url, headers=self.getHeaders())).read())
print request
request = requests.get(url, headers=self.getHeaders()).json()
self.logger.info('Connected to the Plex Media Server %s at %s' % (request.get('friendlyName'), url))
return True
except Exception as e:
self.logger.debug('headers %s' % self.getHeaders())
self.logger.error('Unable to contact Plex via %s error %s' % (url, e))
return

@@ -131,11 +129,11 @@ def GetRecentMovies(self, limit=5):
plex_hide_homemovies = htpc.settings.get('plex_hide_homemovies', False)
movies = []

for section in self.JsonLoader(urlopen(Request('%s/library/sections' % (plex_url), headers=self.getHeaders())).read())['_children']:
for section in self.jloader('%s/library/sections' % plex_url).get('Directory', {}):
if self.check_ignore(section['title']):
if section['type'] == 'movie':
if section['agent'] != 'com.plexapp.agents.none' or not plex_hide_homemovies:
for movie in self.JsonLoader(urlopen(Request('%s/library/sections/%s/all?type=1&sort=addedAt:desc&X-Plex-Container-Start=0&X-Plex-Container-Size=%s' % (plex_url, section['key'], limit), headers=self.getHeaders())).read())['_children']:
for movie in self.jloader('%s/library/sections/%s/all?type=1&sort=addedAt:desc&X-Plex-Container-Start=0&X-Plex-Container-Size=%s' % (plex_url, section['key'], limit)).get('Metadata', {}):
jmovie = {}
genre = []

@@ -178,6 +176,7 @@ def GetRecentMovies(self, limit=5):
@require(member_of(htpc.role_admin))
@cherrypy.tools.json_out()
def primecache(self, disable_pil=0):
# fix me
plex_url = Plex.get_server_url()
headers = self.getHeaders()
imgdir = os.path.join(htpc.DATADIR, 'images/')
@@ -189,8 +188,8 @@ def primecache(self, disable_pil=0):
if use_pil is False:
disable_pil = True

for section in self.JsonLoader(urlopen(Request('%s/library/sections' % (plex_url), headers=headers)).read())['_children']:
for item in self.JsonLoader(urlopen(Request('%s/library/sections/%s/all' % (plex_url, section['key']), headers=headers)).read())['_children']:
for section in self.jloader('%s/library/sections' % plex_url).get('Directory', {}):
for item in self.jloader('%s/library/sections/%s/all' % (plex_url, section['key'])).get('Metadata', {}):

if 'thumb' in item:
d = {}
@@ -242,10 +241,10 @@ def GetRecentShows(self, limit=5):
plex_url = Plex.get_server_url()
episodes = []

for section in self.JsonLoader(urlopen(Request('%s/library/sections' % (plex_url), headers=self.getHeaders())).read())['_children']:
for section in self.jloader('/library/sections').get('Directory', {}):
if self.check_ignore(section['title']):
if section['type'] == 'show':
for episode in self.JsonLoader(urlopen(Request('%s/library/sections/%s/all?type=4&sort=addedAt:desc&X-Plex-Container-Start=0&X-Plex-Container-Size=%s' % (plex_url, section['key'], limit), headers=self.getHeaders())).read())['_children']:
for episode in self.jloader('%s/library/sections/%s/all?type=4&sort=addedAt:desc&X-Plex-Container-Start=0&X-Plex-Container-Size=%s' % (plex_url, section['key'], limit)).get('Metadata', {}):
try:
jepisode = {}

@@ -294,10 +293,10 @@ def GetRecentAlbums(self, limit=5):
plex_url = Plex.get_server_url()
albums = []

for section in self.JsonLoader(urlopen(Request('%s/library/sections' % (plex_url), headers=self.getHeaders())).read())['_children']:
for section in self.jloadr('%s/library/sections' % plex_url).get('Directory', {}):
if self.check_ignore(section['title']):
if section['type'] == 'artist':
for album in self.JsonLoader(urlopen(Request('%s/library/sections/%s/recentlyAdded?X-Plex-Container-Start=0&X-Plex-Container-Size=%s' % (plex_url, section['key'], limit), headers=self.getHeaders())).read())['_children']:
for album in self.jloader('%s/library/sections/%s/recentlyAdded?X-Plex-Container-Start=0&X-Plex-Container-Size=%s' % (plex_url, section['key'], limit)).get('Metadata', {}):
jalbum = {}

jalbum['title'] = album['title']
@@ -365,12 +364,11 @@ def GetMovies(self, start=0, end=0, hidewatched=0, f=''):
hidewatched = 'all'

f = self._filter(f)
self.logger.debug('_filter response was %s' % f)
for section in self.jloader('%s/library/sections' % (plex_url)):
for section in self.jloader('%s/library/sections' % plex_url).get('Directory', {}):
if self.check_ignore(section['title']):
if section['type'] == 'movie':
if section['agent'] != 'com.plexapp.agents.none' or not plex_hide_homemovies:
for movie in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)):
for movie in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)).get('Metadata', {}):
if movie['title'] not in dupe_check:
dupe_check.append(movie['title'])

@@ -417,14 +415,8 @@ def GetMovies(self, start=0, end=0, hidewatched=0, f=''):
# for pms 1.3
if 'Genre' in movie:
genre = [t.get('tag') for t in movie['Genre']]
# pre 1.3
if '_children' in movie:
for attrib in movie['_children']:
if attrib['_elementType'] == 'Genre':
genre.append(attrib['tag'])

# type >= 1.3
jmovie['type'] = movie.get('type', movie.get('_elementType', ''))
jmovie['type'] = movie.get('type', '')

if genre:
jmovie['genre'] = genre
@@ -444,10 +436,7 @@ def GetMovies(self, start=0, end=0, hidewatched=0, f=''):

return {'limits': limits, 'movies': sortedmovies[int(start):int(end)]}
except Exception as e:
self.logger.exception(e)
self.logger.error('Unable to fetch all movies! Exception: %s' % e)
#return self.JsonLoader(urlopen(Request('%s/library/sections' % (plex_url), headers=self.getHeaders())).read())
#return self.JsonLoader(urlopen(Request('%s/library/sections' % (plex_url), headers=self.getHeaders())).read()).get('MediaContainer').get('Metadata')
return

@cherrypy.expose()
@@ -470,30 +459,24 @@ def GetShows(self, start=0, end=0, hidewatched=0, f=''):
f = self._filter(f)
self.logger.debug('_filter response was %s' % f)

for section in self.jloader('%s/library/sections' % plex_url):
for section in self.jloader('%s/library/sections' % plex_url).get('Directory', {}):
if self.check_ignore(section['title']):
if section['type'] == 'show':
try:
for tvShow in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)):
#print('%s/library/sections/%s/%s' % (plex_url, section['key'], f))
for tvShow in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)).get('Metadata'):
# Only allow unique showname in dupecheck
if tvShow['title'] not in dupe_check:
dupe_check.append(tvShow['title'])
jshow = {}
genre = []
jshow['type'] = tvShow.get('type', tvShow.get('_elementType', ''))
jshow['type'] = tvShow.get('type')
jshow['itemcount'] = 0
jshow['playcount'] = 0

# for pms 1.3
if 'Genre' in tvShow:
genre = [t.get('tag') for t in tvShow['Genre']]

if '_children' in tvShow:
for attrib in tvShow['_children']:
if attrib['_elementType'] == 'Genre':
genre.append(attrib['tag'])

# Since titleSort only exist in titles like the showname etc
# Set title as titlesort
if 'titleSort' not in tvShow:
@@ -527,6 +510,9 @@ def GetShows(self, start=0, end=0, hidewatched=0, f=''):
except socket.timeout:
continue

except Exception as e:
self.logger.exception('%s' % e)

limits['start'] = int(start)
limits['total'] = len(tvShows)
limits['end'] = int(end)
@@ -555,15 +541,14 @@ def GetArtists(self, start=0, end=0, f=''):
sortedartist = []

f = self._filter(f)
self.logger.debug('_filter response was %s' % f)

for section in self.jloader('%s/library/sections' % plex_url):
for section in self.jloader('%s/library/sections' % plex_url).get('Directory', {}):
if self.check_ignore(section['title']):
if section['type'] == 'artist':
for artist in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)):
for artist in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)).get('Metadata'):
if artist['title'] not in dupe_check:
jartist = {}
jartist['type'] = artist.get('type', artist.get('_elementType', ''))
jartist['type'] = artist.get('type' '')
dupe_check.append(artist['title'])
# Since titleSort only exist in titles like the xx etc
# Set title as titlesort
@@ -612,10 +597,10 @@ def GetAlbums(self, start=0, end=0, artistid='', f=''):
f += '&type=9'
self.logger.debug('_filter response was %s' % f)

for section in self.jloader('%s/library/sections' % plex_url):
for section in self.jloader('%s/library/sections' % plex_url).get('Directory', {}):
if self.check_ignore(section['title']):
if section['type'] == 'artist':
for album in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)):
for album in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)).get('Metadata', {}):
if (str(album['parentRatingKey']) == artistid) or (artistid == ''):
jalbum = {}

@@ -653,12 +638,6 @@ def check_ignore(self, name):
else:
return True

@cherrypy.expose()
@cherrypy.tools.json_out()
def inspector(self, url=''):
if url:
return self.jloader(url)

@cherrypy.expose()
@require()
@cherrypy.tools.json_out()
@@ -679,16 +658,11 @@ def GetSongs(self, start=0, end=0, albumid='', f=''):
f += '&type=10'

if albumid != '':
#request = self.jloader('%s/library/metadata/%s/children' % (plex_url, albumid))
request = self.jloader('%s/library/metadata/%s/children' % (plex_url, albumid))
print('%s/library/metadata/%s/children' % (plex_url, albumid))
#print request

for i in range(1):
#for song in request:
for song in request.get('Metadata', {}):
jsong = {}

'''
try:
jsong['artist'] = song['originalTitle']
except:
@@ -703,31 +677,14 @@ def GetSongs(self, start=0, end=0, albumid='', f=''):
jsong['duration'] = song['duration'] / 1000
except:
pass
'''

try:
jsong['artist'] = request['originalTitle']
except:
jsong['artist'] = request['title1']

jsong['label'] = request['title']

jsong['album'] = request['parentTitle']

jsong['id'] = request['ratingKey']
try:
jsong['duration'] = request['duration'] / 1000
except:
pass

songs.append(jsong)
else:
for section in self.jloader('%s/library/sections' % plex_url):
for section in self.jloader('%s/library/sections' % plex_url).get('Directory', {}):
# Only check file paths once!
if section['title'] not in htpc.settings.get('plex_ignore_sections', '').split():
if section['type'] == 'artist':
self.logger.debug('%s/library/sections/%s/%s' % (plex_url, section['key'], f))
for song in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)):
for song in self.jloader('%s/library/sections/%s/%s' % (plex_url, section['key'], f)).get('Metadata', {}):
jsong = {}
jsong['dumpz'] = song
if 'grandparentTitle' or 'title' in song:
@@ -749,37 +706,25 @@ def GetSongs(self, start=0, end=0, albumid='', f=''):
limits['end'] = len(songs)

return {'limits': limits, 'songs': songs[int(start):int(end)]}
except Exception as e:
self.logger.error('Unable to fetch all songs! Exception: %s' % e)
except:
self.logger.exception('Unable to fetch all songs!')
return

def jloader(self, url, method='get', headers=None, rtype='text'):
r = getattr(requests, method)

if headers is None:
headers = self.getHeaders()
# mZaRtAxbzEU1gTx2ENSs

#print headers

r = r(url, headers=headers)

try:
if r.status_code == 401:
t = self.myPlexSignin()
print t

#print r.content
j = r.json()
return j
return j.get('MediaContainer', {})

return j['MediaContainer']['Metadata']
#except HTTPError:
except KeyError:
try:
return r.json()['_children']
except Exception as e:
pass
except Exception as e:
self.logger.exception('Failed to %s %s' % (method, url))
return {}
@@ -795,7 +740,7 @@ def GetEpisodes(self, start=0, end=0, tvshowid=None, hidewatched=0, f=''):
episodes = []
limits = {}

for episode in self.JsonLoader(urlopen(Request('%s/library/metadata/%s/allLeaves' % (plex_url, tvshowid), headers=self.getHeaders())).read())['_children']:
for episode in self.jloader('%s/library/metadata/%s/allLeaves' % (plex_url, tvshowid)).get('Metadata', {}):
jepisode = {}
jepisode['playcount'] = 0

@@ -969,59 +914,63 @@ def NowPlaying(self):

try:
plex_url = Plex.get_server_url()
self.logger.error(plex_url)

print('%s/status/sessions' % plex_url)

for video in self.jloader('%s/status/sessions' % plex_url):

#for video in self.JsonLoader(urlopen(Request('%s/status/sessions' % (plex_url), headers=self.getHeaders())).read())['_children']:
jplaying_item = {}
jplaying_item['protocolCapabilities'] = []

if 'index' in video:
jplaying_item['episode'] = int(video['index'])
if 'parentThumb' in video:
jplaying_item['fanart'] = video['parentThumb']
jplaying_item['thumbnail'] = video['thumb']
if 'parentIndex' in video:
jplaying_item['season'] = int(video['parentIndex'])
jplaying_item['title'] = video['title']
if 'year' in video:
jplaying_item['year'] = int(video['year'])
jplaying_item['id'] = int(video['ratingKey'])
jplaying_item['type'] = video['type']
if 'grandparentTitle' in video:
jplaying_item['show'] = video['grandparentTitle']
jplaying_item['duration'] = int(video['duration'])
try:
jplaying_item['viewOffset'] = int(video['viewOffset'])
except:
jplaying_item['viewOffset'] = 0

for children in video['_children']:
if children['_elementType'] == 'Player':
jplaying_item['state'] = children['state']
jplaying_item['player'] = children['title']
# We need some more info to see what the client supports
for client in self.JsonLoader(urlopen(Request('%s/clients' % (plex_url), headers=self.getHeaders())).read())['_children']:
if client['machineIdentifier'] == children['machineIdentifier']:
jplaying_item['protocolCapabilities'] = client['protocolCapabilities'].split(',')
jplaying_item['address'] = client['address']

if children['_elementType'] == 'User':
if 'title' in children:
jplaying_item['user'] = children['title']
if 'thumb' in children:
jplaying_item['avatar'] = children['thumb']

# Sometimes the client doesn't send the last timeline event. Ignore all client that almost have played the entire lenght.
if jplaying_item['viewOffset'] < (int(jplaying_item['duration']) - 10000):
playing_items.append(jplaying_item)

except Exception as e:
self.logger.error('Unable to fetch currently playing information! Exception: %s' % e)
pass
result = self.jloader('%s/status/sessions' % plex_url)

for t in ['Video', 'Track']:
res = result.get(t, [])

for video in res:
jplaying_item = {}
jplaying_item['protocolCapabilities'] = []

if 'index' in video:
jplaying_item['episode'] = int(video['index'])
if 'parentThumb' in video:
jplaying_item['fanart'] = video['parentThumb']
jplaying_item['thumbnail'] = video['thumb']
if 'parentIndex' in video:
jplaying_item['season'] = int(video['parentIndex'])
jplaying_item['title'] = video['title']

if 'year' in video:
jplaying_item['year'] = int(video['year'])

jplaying_item['id'] = int(video['ratingKey'])
jplaying_item['type'] = video['type']

if 'grandparentTitle' in video:
jplaying_item['show'] = video['grandparentTitle']
jplaying_item['duration'] = int(video['duration'])

try:
jplaying_item['viewOffset'] = int(video['viewOffset'])
except:
jplaying_item['viewOffset'] = 0

jplaying_item['state'] = video.get('Player', {}).get('state')
jplaying_item['player'] = video.get('Player', {}).get('title')
jplaying_item['machineIdentifier'] = video.get('Player', {}).get('machineIdentifier')

# We need some more info to see what the client supports
for client in self.jloader('%s/clients' % plex_url).get('Server', []):
if client['machineIdentifier'] == jplaying_item['machineIdentifier']:
jplaying_item['protocolCapabilities'] = client['protocolCapabilities'].split(',')
# we need this adress since we need a local ip
jplaying_item['address'] = client['address']

jplaying_item['user'] = video.get('User', {}).get('title')
user_thumb = video.get('User', {}).get('thumb')
if user_thumb:
jplaying_item['avatar'] = user_thumb

# Sometimes the client doesn't send the last timeline event. Ignore all client that almost have played the entire lenght.
if jplaying_item['viewOffset'] < (int(jplaying_item['duration']) - 10000):
playing_items.append(jplaying_item)

except:
self.logger.exception('Unable to fetch currently playing information!')

return {'playing_items': playing_items}

@cherrypy.expose()
@@ -1033,11 +982,11 @@ def UpdateLibrary(self, section_type=None):
try:
plex_url = Plex.get_server_url()

for section in self.JsonLoader(urlopen(Request('%s/library/sections' % (plex_url), headers=self.getHeaders())).read())['_children']:
for section in self.jloader('%s/library/sections' % plex_url).get('Directory'):
if section_type is None or section_type == section['type']:
self.logger.debug('Updating section %s' % section['key'])
try:
urllib.urlopen('%s/library/sections/%s/refresh' % (plex_url, section['key']))
requests.get('%s/library/sections/%s/refresh' % (plex_url, section['key']), headers=self.getHeaders())
except Exception as e:
self.logger.error('Failed to update section %s on Plex: %s' % (section['key'], e))
return 'Update command sent to Plex'
@@ -1051,18 +1000,23 @@ def UpdateLibrary(self, section_type=None):
def ControlPlayer(self, player, action, value=''): # TODO fix me
''' Various commands to control Plex Player '''
self.logger.debug('Sending %s to %s value %s: ' % (action, player, value))
self._commandId += 1

h = self.getHeaders()
h['commandId'] = self._commandId

try:
self.navigationCommands = ['moveUp', 'moveDown', 'moveLeft', 'moveRight', 'pageUp', 'pageDown', 'nextLetter', 'previousLetter', 'select', 'back', 'contextMenu', 'toggleOSD']
self.playbackCommands = ['play', 'pause', 'stop', 'rewind', 'fastForward', 'stepForward', 'bigStepForward', 'stepBack', 'bigStepBack', 'skipNext', 'skipPrevious']
self.applicationCommands = ['playFile', 'playMedia', 'screenshot', 'sendString', 'sendKey', 'sendVirtualKey', 'setVolume']

plex_url = Plex.get_server_url()
if action in self.navigationCommands:
urllib.urlopen('%s/system/players/%s/naviation/%s' % (plex_url, player, action))
requests.get('%s/system/players/%s/naviation/%s' % (plex_url, player, action), headers=h)
elif action in self.playbackCommands:
urllib.urlopen('%s/system/players/%s/playback/%s' % (plex_url, player, action))
r = requests.get('%s/player/%s/playback/%s' % (plex_url, player, action), headers=h)
elif action.split('?')[0] in self.applicationCommands:
urllib.urlopen('%s/system/players/%s/application/%s' % (plex_url, player, action))
requests.get('%s/system/players/%s/application/%s' % (plex_url, player, action), headers=h)
else:
raise ValueError('Unable to control Plex with action: %s' % action)

@@ -1081,12 +1035,7 @@ def GetPlayers(self, filter=None):

plex_url = Plex.get_server_url()
players = []
for player in self.JsonLoader(urlopen(Request('%s/clients' % (plex_url), headers=self.getHeaders())).read())['_children']:

try:
del player['_elementType']
except:
pass
for player in self.jloader('%s/clients' % plex_url).get('Servers', []):

if 'protocolCapabilities' in player:
player['protocolCapabilities'] = player['protocolCapabilities'].split(',')