Redesign playlists
This commit is contained in:
parent
5d79bcf1c2
commit
8a58c885e9
6 changed files with 276 additions and 134 deletions
|
@ -14,7 +14,10 @@ from plexbmchelper import listener, plexgdm, subscribers, functions, \
|
||||||
@utils.ThreadMethodsAdditionalSuspend('plex_serverStatus')
|
@utils.ThreadMethodsAdditionalSuspend('plex_serverStatus')
|
||||||
@utils.ThreadMethods
|
@utils.ThreadMethods
|
||||||
class PlexCompanion(threading.Thread):
|
class PlexCompanion(threading.Thread):
|
||||||
def __init__(self):
|
"""
|
||||||
|
Initialize with a Queue for callbacks
|
||||||
|
"""
|
||||||
|
def __init__(self, queue):
|
||||||
self.logMsg("----===## Starting PlexCompanion ##===----", 1)
|
self.logMsg("----===## Starting PlexCompanion ##===----", 1)
|
||||||
self.settings = settings.getSettings()
|
self.settings = settings.getSettings()
|
||||||
|
|
||||||
|
@ -24,6 +27,8 @@ class PlexCompanion(threading.Thread):
|
||||||
self.logMsg("Registration string is: %s "
|
self.logMsg("Registration string is: %s "
|
||||||
% self.client.getClientDetails(), 2)
|
% self.client.getClientDetails(), 2)
|
||||||
|
|
||||||
|
self.queue = queue
|
||||||
|
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -51,6 +56,7 @@ class PlexCompanion(threading.Thread):
|
||||||
subscriptionManager,
|
subscriptionManager,
|
||||||
jsonClass,
|
jsonClass,
|
||||||
self.settings,
|
self.settings,
|
||||||
|
self.queue,
|
||||||
('', self.settings['myport']),
|
('', self.settings['myport']),
|
||||||
listener.MyHandler)
|
listener.MyHandler)
|
||||||
httpd.timeout = 0.95
|
httpd.timeout = 0.95
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
from urlparse import urlparse, parse_qs
|
from urlparse import urlparse, parse_qsl
|
||||||
import re
|
import re
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
|
@ -86,13 +86,13 @@ def GetPlexKeyNumber(plexKey):
|
||||||
def ParseContainerKey(containerKey):
|
def ParseContainerKey(containerKey):
|
||||||
"""
|
"""
|
||||||
Parses e.g. /playQueues/3045?own=1&repeat=0&window=200 to:
|
Parses e.g. /playQueues/3045?own=1&repeat=0&window=200 to:
|
||||||
'playQueues', '3045', {'window': ['200'], 'own': ['1'], 'repeat': ['0']}
|
'playQueues', '3045', {'window': '200', 'own': '1', 'repeat': '0'}
|
||||||
|
|
||||||
Output hence: library, key, query (query as a special dict)
|
Output hence: library, key, query (str, str, dict)
|
||||||
"""
|
"""
|
||||||
result = urlparse(containerKey)
|
result = urlparse(containerKey)
|
||||||
library, key = GetPlexKeyNumber(result.path)
|
library, key = GetPlexKeyNumber(result.path)
|
||||||
query = parse_qs(result.query)
|
query = dict(parse_qsl(result.query))
|
||||||
return library, key, query
|
return library, key, query
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,22 +24,37 @@ class Playlist():
|
||||||
def __init__(self, typus=None):
|
def __init__(self, typus=None):
|
||||||
self.userid = utils.window('currUserId')
|
self.userid = utils.window('currUserId')
|
||||||
self.server = utils.window('pms_server')
|
self.server = utils.window('pms_server')
|
||||||
|
# Construct the Kodi playlist instance
|
||||||
if typus == 'video':
|
if typus == 'video':
|
||||||
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
self.typus = 'video'
|
||||||
self.logMsg('Initiated video playlist', 1)
|
self.logMsg('Initiated video playlist', 1)
|
||||||
elif typus == 'music':
|
elif typus == 'music':
|
||||||
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
||||||
|
self.typus = 'music'
|
||||||
self.logMsg('Initiated music playlist', 1)
|
self.logMsg('Initiated music playlist', 1)
|
||||||
else:
|
else:
|
||||||
self.playlist = None
|
self.playlist = None
|
||||||
|
self.typus = None
|
||||||
if self.playlist is not None:
|
if self.playlist is not None:
|
||||||
self.playlistId = self.playlist.getPlayListId()
|
self.playlistId = self.playlist.getPlayListId()
|
||||||
|
# "interal" PKC playlist
|
||||||
|
self.items = []
|
||||||
|
|
||||||
def _initiatePlaylist(self, itemids):
|
def clear(self):
|
||||||
|
"""
|
||||||
|
Empties current Kodi playlist and internal self.items list
|
||||||
|
"""
|
||||||
|
self.logMsg('Clearing playlist', 1)
|
||||||
|
self.playlist.clear()
|
||||||
|
self.items = []
|
||||||
|
|
||||||
|
def _initiatePlaylist(self):
|
||||||
self.logMsg('Initiating playlist', 1)
|
self.logMsg('Initiating playlist', 1)
|
||||||
playlist = None
|
playlist = None
|
||||||
with embydb.GetEmbyDB() as emby_db:
|
with embydb.GetEmbyDB() as emby_db:
|
||||||
for itemid in itemids:
|
for item in self.items:
|
||||||
|
itemid = item['plexId']
|
||||||
embydb_item = emby_db.getItem_byId(itemid)
|
embydb_item = emby_db.getItem_byId(itemid)
|
||||||
try:
|
try:
|
||||||
mediatype = embydb_item[4]
|
mediatype = embydb_item[4]
|
||||||
|
@ -54,63 +69,95 @@ class Playlist():
|
||||||
if PlexAPI.API(item[0]).getType() == 'track':
|
if PlexAPI.API(item[0]).getType() == 'track':
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
||||||
self.logMsg('Music playlist initiated', 1)
|
self.logMsg('Music playlist initiated', 1)
|
||||||
|
self.typus = 'music'
|
||||||
else:
|
else:
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
self.logMsg('Video playlist initiated', 1)
|
self.logMsg('Video playlist initiated', 1)
|
||||||
|
self.typus = 'video'
|
||||||
else:
|
else:
|
||||||
if mediatype == 'song':
|
if mediatype == 'song':
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
||||||
self.logMsg('Music playlist initiated', 1)
|
self.logMsg('Music playlist initiated', 1)
|
||||||
|
self.typus = 'music'
|
||||||
else:
|
else:
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
self.logMsg('Video playlist initiated', 1)
|
self.logMsg('Video playlist initiated', 1)
|
||||||
|
self.typus = 'video'
|
||||||
break
|
break
|
||||||
self.playlist = playlist
|
self.playlist = playlist
|
||||||
if self.playlist is not None:
|
if self.playlist is not None:
|
||||||
self.playlistId = self.playlist.getPlayListId()
|
self.playlistId = self.playlist.getPlayListId()
|
||||||
|
|
||||||
def _addToPlaylist(self, itemids, startPlayer=False):
|
def _addToPlaylist(self, startitem, startPlayer=False):
|
||||||
started = False
|
started = False
|
||||||
with embydb.GetEmbyDB() as emby_db:
|
with embydb.GetEmbyDB() as emby_db:
|
||||||
for itemid in itemids:
|
for pos, item in enumerate(self.items):
|
||||||
embydb_item = emby_db.getItem_byId(itemid)
|
kodiId = None
|
||||||
|
plexId = item['plexId']
|
||||||
|
embydb_item = emby_db.getItem_byId(plexId)
|
||||||
try:
|
try:
|
||||||
dbid = embydb_item[0]
|
kodiId = embydb_item[0]
|
||||||
mediatype = embydb_item[4]
|
mediatype = embydb_item[4]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg('Couldnt find item %s in Kodi db' % itemid, 1)
|
self.logMsg('Couldnt find item %s in Kodi db' % plexId, 1)
|
||||||
item = PlexFunctions.GetPlexMetadata(itemid)
|
xml = PlexFunctions.GetPlexMetadata(plexId)
|
||||||
if item in (None, 401):
|
if xml in (None, 401):
|
||||||
self.logMsg('Could not download itemid %s'
|
self.logMsg('Could not download plexId %s'
|
||||||
% itemid, -1)
|
% plexId, -1)
|
||||||
else:
|
else:
|
||||||
self.logMsg('Downloaded item metadata, adding now', 1)
|
self.logMsg('Downloaded xml metadata, adding now', 1)
|
||||||
self._addtoPlaylist_xbmc(item[0])
|
self._addtoPlaylist_xbmc(xml[0])
|
||||||
else:
|
else:
|
||||||
# Add to playlist
|
# Add to playlist
|
||||||
self.logMsg("Adding %s PlexId %s, KodiId %s to playlist."
|
self.logMsg("Adding %s PlexId %s, KodiId %s to playlist."
|
||||||
% (mediatype, itemid, dbid), 1)
|
% (mediatype, plexId, kodiId), 1)
|
||||||
self.addtoPlaylist(dbid, mediatype)
|
self.addtoPlaylist(kodiId, mediatype)
|
||||||
if started is False and startPlayer is True:
|
# Add the kodiId
|
||||||
|
if kodiId is not None:
|
||||||
|
item['kodiId'] = str(kodiId)
|
||||||
|
if (started is False and
|
||||||
|
startPlayer is True and
|
||||||
|
startitem[1] == item[startitem[0]]):
|
||||||
started = True
|
started = True
|
||||||
xbmc.Player().play(self.playlist)
|
xbmc.Player().play(self.playlist, startpos=pos)
|
||||||
|
if (started is False and
|
||||||
|
startPlayer is True and
|
||||||
|
len(self.playlist) > 0):
|
||||||
|
self.logMsg('Never received a starting item for playlist, '
|
||||||
|
'starting with the first entry', 1)
|
||||||
|
xbmc.Player().play(self.playlist)
|
||||||
|
|
||||||
def playAll(self, itemids, startat):
|
def playAll(self, items, startitem, offset):
|
||||||
|
"""
|
||||||
|
items: list of dicts of the form
|
||||||
|
{
|
||||||
|
'queueId': Plex playQueueItemID, e.g. '29175'
|
||||||
|
'plexId': Plex ratingKey, e.g. '125'
|
||||||
|
'kodiId': Kodi's db id of the same item
|
||||||
|
}
|
||||||
|
|
||||||
|
startitem: tuple (typus, id), where typus is either 'queueId' or
|
||||||
|
'plexId' and id is the corresponding id as a string
|
||||||
|
offset: First item's time offset to play in Kodi time (an int)
|
||||||
|
"""
|
||||||
self.logMsg("---*** PLAY ALL ***---", 1)
|
self.logMsg("---*** PLAY ALL ***---", 1)
|
||||||
self.logMsg("Items: %s and start at: %s" % (itemids, startat), 1)
|
self.logMsg('Startitem: %s, offset: %s, items: %s'
|
||||||
|
% (startitem, offset, items), 1)
|
||||||
|
self.items = items
|
||||||
if self.playlist is None:
|
if self.playlist is None:
|
||||||
self._initiatePlaylist(itemids)
|
self._initiatePlaylist()
|
||||||
if self.playlist is None:
|
if self.playlist is None:
|
||||||
self.logMsg('Could not create playlist, abort', -1)
|
self.logMsg('Could not create playlist, abort', -1)
|
||||||
return
|
return
|
||||||
|
|
||||||
utils.window('plex_customplaylist', value="true")
|
utils.window('plex_customplaylist', value="true")
|
||||||
if startat != 0:
|
if offset != 0:
|
||||||
# Seek to the starting position
|
# Seek to the starting position
|
||||||
utils.window('plex_customplaylist.seektime', str(startat))
|
utils.window('plex_customplaylist.seektime', str(offset))
|
||||||
self._addToPlaylist(itemids, startPlayer=True)
|
self._addToPlaylist(startitem, startPlayer=True)
|
||||||
|
# Log playlist
|
||||||
self.verifyPlaylist()
|
self.verifyPlaylist()
|
||||||
|
self.logMsg('Internal playlist: %s' % self.items, 2)
|
||||||
|
|
||||||
def modifyPlaylist(self, itemids):
|
def modifyPlaylist(self, itemids):
|
||||||
self.logMsg("---*** ADD TO PLAYLIST ***---", 1)
|
self.logMsg("---*** ADD TO PLAYLIST ***---", 1)
|
||||||
|
@ -126,7 +173,6 @@ class Playlist():
|
||||||
mediatype: Kodi type: 'movie', 'episode', 'musicvideo', 'artist',
|
mediatype: Kodi type: 'movie', 'episode', 'musicvideo', 'artist',
|
||||||
'album', 'song', 'genre'
|
'album', 'song', 'genre'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pl = {
|
pl = {
|
||||||
'jsonrpc': "2.0",
|
'jsonrpc': "2.0",
|
||||||
'id': 1,
|
'id': 1,
|
||||||
|
|
|
@ -106,11 +106,6 @@ class jsonClass():
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": "JSONRPC.Ping"
|
"method": "JSONRPC.Ping"
|
||||||
})
|
})
|
||||||
elif action.lower() == "playmedia":
|
|
||||||
xbmc.Player().play("plugin://plugin.video.plexkodiconnect/"
|
|
||||||
"?mode=companion&arguments=%s"
|
|
||||||
% arguments)
|
|
||||||
return True
|
|
||||||
elif arguments:
|
elif arguments:
|
||||||
request = json.dumps({
|
request = json.dumps({
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
|
|
@ -73,9 +73,10 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
def answer_request(self, sendData):
|
def answer_request(self, sendData):
|
||||||
self.serverlist = self.server.client.getServerList()
|
self.serverlist = self.server.client.getServerList()
|
||||||
self.subMgr = self.server.subscriptionManager
|
subMgr = self.server.subscriptionManager
|
||||||
self.js = self.server.jsonClass
|
js = self.server.jsonClass
|
||||||
self.settings = self.server.settings
|
settings = self.server.settings
|
||||||
|
queue = self.server.queue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
request_path = self.path[1:]
|
request_path = self.path[1:]
|
||||||
|
@ -87,17 +88,17 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||||
params[key] = paramarrays[key][0]
|
params[key] = paramarrays[key][0]
|
||||||
self.logMsg("remote request_path: %s" % request_path, 2)
|
self.logMsg("remote request_path: %s" % request_path, 2)
|
||||||
self.logMsg("params received from remote: %s" % params, 2)
|
self.logMsg("params received from remote: %s" % params, 2)
|
||||||
self.subMgr.updateCommandID(self.headers.get(
|
subMgr.updateCommandID(self.headers.get(
|
||||||
'X-Plex-Client-Identifier',
|
'X-Plex-Client-Identifier',
|
||||||
self.client_address[0]),
|
self.client_address[0]),
|
||||||
params.get('commandID', False))
|
params.get('commandID', False))
|
||||||
if request_path == "version":
|
if request_path == "version":
|
||||||
self.response(
|
self.response(
|
||||||
"PlexKodiConnect Plex Companion: Running\r\nVersion: %s"
|
"PlexKodiConnect Plex Companion: Running\r\nVersion: %s"
|
||||||
% self.settings['version'])
|
% settings['version'])
|
||||||
elif request_path == "verify":
|
elif request_path == "verify":
|
||||||
self.response("XBMC JSON connection test:\r\n" +
|
self.response("XBMC JSON connection test:\r\n" +
|
||||||
self.js.jsonrpc("ping"))
|
js.jsonrpc("ping"))
|
||||||
elif "resources" == request_path:
|
elif "resources" == request_path:
|
||||||
resp = ('%s'
|
resp = ('%s'
|
||||||
'<MediaContainer>'
|
'<MediaContainer>'
|
||||||
|
@ -114,24 +115,24 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||||
'/>'
|
'/>'
|
||||||
'</MediaContainer>'
|
'</MediaContainer>'
|
||||||
% (getXMLHeader(),
|
% (getXMLHeader(),
|
||||||
self.settings['client_name'],
|
settings['client_name'],
|
||||||
self.settings['uuid'],
|
settings['uuid'],
|
||||||
self.settings['platform'],
|
settings['platform'],
|
||||||
self.settings['plexbmc_version']))
|
settings['plexbmc_version']))
|
||||||
self.logMsg("crafted resources response: %s" % resp, 2)
|
self.logMsg("crafted resources response: %s" % resp, 2)
|
||||||
self.response(resp, self.js.getPlexHeaders())
|
self.response(resp, js.getPlexHeaders())
|
||||||
elif "/subscribe" in request_path:
|
elif "/subscribe" in request_path:
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
protocol = params.get('protocol', False)
|
protocol = params.get('protocol', False)
|
||||||
host = self.client_address[0]
|
host = self.client_address[0]
|
||||||
port = params.get('port', False)
|
port = params.get('port', False)
|
||||||
uuid = self.headers.get('X-Plex-Client-Identifier', "")
|
uuid = self.headers.get('X-Plex-Client-Identifier', "")
|
||||||
commandID = params.get('commandID', 0)
|
commandID = params.get('commandID', 0)
|
||||||
self.subMgr.addSubscriber(protocol,
|
subMgr.addSubscriber(protocol,
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
uuid,
|
uuid,
|
||||||
commandID)
|
commandID)
|
||||||
elif "/poll" in request_path:
|
elif "/poll" in request_path:
|
||||||
if params.get('wait', False) == '1':
|
if params.get('wait', False) == '1':
|
||||||
sleep(950)
|
sleep(950)
|
||||||
|
@ -139,28 +140,28 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||||
self.response(
|
self.response(
|
||||||
re.sub(r"INSERTCOMMANDID",
|
re.sub(r"INSERTCOMMANDID",
|
||||||
str(commandID),
|
str(commandID),
|
||||||
self.subMgr.msg(self.js.getPlayers())),
|
subMgr.msg(js.getPlayers())),
|
||||||
{
|
{
|
||||||
'X-Plex-Client-Identifier': self.settings['uuid'],
|
'X-Plex-Client-Identifier': settings['uuid'],
|
||||||
'Access-Control-Expose-Headers':
|
'Access-Control-Expose-Headers':
|
||||||
'X-Plex-Client-Identifier',
|
'X-Plex-Client-Identifier',
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Content-Type': 'text/xml'
|
'Content-Type': 'text/xml'
|
||||||
})
|
})
|
||||||
elif "/unsubscribe" in request_path:
|
elif "/unsubscribe" in request_path:
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
uuid = self.headers.get('X-Plex-Client-Identifier', False) \
|
uuid = self.headers.get('X-Plex-Client-Identifier', False) \
|
||||||
or self.client_address[0]
|
or self.client_address[0]
|
||||||
self.subMgr.removeSubscriber(uuid)
|
subMgr.removeSubscriber(uuid)
|
||||||
elif request_path == "player/playback/setParameters":
|
elif request_path == "player/playback/setParameters":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
if 'volume' in params:
|
if 'volume' in params:
|
||||||
volume = int(params['volume'])
|
volume = int(params['volume'])
|
||||||
self.logMsg("adjusting the volume to %s%%" % volume, 2)
|
self.logMsg("adjusting the volume to %s%%" % volume, 2)
|
||||||
self.js.jsonrpc("Application.SetVolume",
|
js.jsonrpc("Application.SetVolume",
|
||||||
{"volume": volume})
|
{"volume": volume})
|
||||||
elif "/playMedia" in request_path:
|
elif "/playMedia" in request_path:
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
offset = params.get('viewOffset', params.get('offset', "0"))
|
offset = params.get('viewOffset', params.get('offset', "0"))
|
||||||
protocol = params.get('protocol', "http")
|
protocol = params.get('protocol', "http")
|
||||||
address = params.get('address', self.client_address[0])
|
address = params.get('address', self.client_address[0])
|
||||||
|
@ -174,90 +175,93 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||||
playQueueID = self.regex.findall(containerKey)[0]
|
playQueueID = self.regex.findall(containerKey)[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
playQueueID = ''
|
playQueueID = ''
|
||||||
|
# We need to tell service.py
|
||||||
self.js.jsonrpc("playmedia", params)
|
queue.put({
|
||||||
self.subMgr.lastkey = params['key']
|
'action': 'playlist',
|
||||||
self.subMgr.containerKey = containerKey
|
'data': params
|
||||||
self.subMgr.playQueueID = playQueueID
|
})
|
||||||
self.subMgr.server = server.get('server', 'localhost')
|
subMgr.lastkey = params['key']
|
||||||
self.subMgr.port = port
|
subMgr.containerKey = containerKey
|
||||||
self.subMgr.protocol = protocol
|
subMgr.playQueueID = playQueueID
|
||||||
self.subMgr.notify()
|
subMgr.server = server.get('server', 'localhost')
|
||||||
|
subMgr.port = port
|
||||||
|
subMgr.protocol = protocol
|
||||||
|
subMgr.notify()
|
||||||
elif request_path == "player/playback/play":
|
elif request_path == "player/playback/play":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.PlayPause",
|
js.jsonrpc("Player.PlayPause",
|
||||||
{"playerid": playerid, "play": True})
|
{"playerid": playerid, "play": True})
|
||||||
elif request_path == "player/playback/pause":
|
elif request_path == "player/playback/pause":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.PlayPause",
|
js.jsonrpc("Player.PlayPause",
|
||||||
{"playerid": playerid, "play": False})
|
{"playerid": playerid, "play": False})
|
||||||
elif request_path == "player/playback/stop":
|
elif request_path == "player/playback/stop":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.Stop", {"playerid": playerid})
|
js.jsonrpc("Player.Stop", {"playerid": playerid})
|
||||||
elif request_path == "player/playback/seekTo":
|
elif request_path == "player/playback/seekTo":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.Seek",
|
js.jsonrpc("Player.Seek",
|
||||||
{"playerid": playerid,
|
{"playerid": playerid,
|
||||||
"value": millisToTime(
|
"value": millisToTime(
|
||||||
params.get('offset', 0))})
|
params.get('offset', 0))})
|
||||||
self.subMgr.notify()
|
subMgr.notify()
|
||||||
elif request_path == "player/playback/stepForward":
|
elif request_path == "player/playback/stepForward":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.Seek",
|
js.jsonrpc("Player.Seek",
|
||||||
{"playerid": playerid,
|
{"playerid": playerid,
|
||||||
"value": "smallforward"})
|
"value": "smallforward"})
|
||||||
self.subMgr.notify()
|
subMgr.notify()
|
||||||
elif request_path == "player/playback/stepBack":
|
elif request_path == "player/playback/stepBack":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.Seek",
|
js.jsonrpc("Player.Seek",
|
||||||
{"playerid": playerid,
|
{"playerid": playerid,
|
||||||
"value": "smallbackward"})
|
"value": "smallbackward"})
|
||||||
self.subMgr.notify()
|
subMgr.notify()
|
||||||
elif request_path == "player/playback/skipNext":
|
elif request_path == "player/playback/skipNext":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.GoTo",
|
js.jsonrpc("Player.GoTo",
|
||||||
{"playerid": playerid,
|
{"playerid": playerid,
|
||||||
"to": "next"})
|
"to": "next"})
|
||||||
self.subMgr.notify()
|
subMgr.notify()
|
||||||
elif request_path == "player/playback/skipPrevious":
|
elif request_path == "player/playback/skipPrevious":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
for playerid in self.js.getPlayerIds():
|
for playerid in js.getPlayerIds():
|
||||||
self.js.jsonrpc("Player.GoTo",
|
js.jsonrpc("Player.GoTo",
|
||||||
{"playerid": playerid,
|
{"playerid": playerid,
|
||||||
"to": "previous"})
|
"to": "previous"})
|
||||||
self.subMgr.notify()
|
subMgr.notify()
|
||||||
elif request_path == "player/playback/skipTo":
|
elif request_path == "player/playback/skipTo":
|
||||||
self.js.skipTo(params.get('key').rsplit('/', 1)[1],
|
js.skipTo(params.get('key').rsplit('/', 1)[1],
|
||||||
params.get('type'))
|
params.get('type'))
|
||||||
self.subMgr.notify()
|
subMgr.notify()
|
||||||
elif request_path == "player/navigation/moveUp":
|
elif request_path == "player/navigation/moveUp":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
self.js.jsonrpc("Input.Up")
|
js.jsonrpc("Input.Up")
|
||||||
elif request_path == "player/navigation/moveDown":
|
elif request_path == "player/navigation/moveDown":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
self.js.jsonrpc("Input.Down")
|
js.jsonrpc("Input.Down")
|
||||||
elif request_path == "player/navigation/moveLeft":
|
elif request_path == "player/navigation/moveLeft":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
self.js.jsonrpc("Input.Left")
|
js.jsonrpc("Input.Left")
|
||||||
elif request_path == "player/navigation/moveRight":
|
elif request_path == "player/navigation/moveRight":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
self.js.jsonrpc("Input.Right")
|
js.jsonrpc("Input.Right")
|
||||||
elif request_path == "player/navigation/select":
|
elif request_path == "player/navigation/select":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
self.js.jsonrpc("Input.Select")
|
js.jsonrpc("Input.Select")
|
||||||
elif request_path == "player/navigation/home":
|
elif request_path == "player/navigation/home":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
self.js.jsonrpc("Input.Home")
|
js.jsonrpc("Input.Home")
|
||||||
elif request_path == "player/navigation/back":
|
elif request_path == "player/navigation/back":
|
||||||
self.response(getOKMsg(), self.js.getPlexHeaders())
|
self.response(getOKMsg(), js.getPlexHeaders())
|
||||||
self.js.jsonrpc("Input.Back")
|
js.jsonrpc("Input.Back")
|
||||||
else:
|
else:
|
||||||
self.logMsg('Unknown request path: %s' % request_path, -1)
|
self.logMsg('Unknown request path: %s' % request_path, -1)
|
||||||
# elif 'player/mirror/details' in request_path:
|
# elif 'player/mirror/details' in request_path:
|
||||||
|
@ -290,7 +294,7 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
daemon_threads = True
|
daemon_threads = True
|
||||||
|
|
||||||
def __init__(self, client, subscriptionManager, jsonClass, settings,
|
def __init__(self, client, subscriptionManager, jsonClass, settings,
|
||||||
*args, **kwargs):
|
queue, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
client: Class handle to plexgdm.plexgdm. We can thus ask for an up-to-
|
client: Class handle to plexgdm.plexgdm. We can thus ask for an up-to-
|
||||||
date serverlist without instantiating anything
|
date serverlist without instantiating anything
|
||||||
|
@ -301,4 +305,5 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
self.subscriptionManager = subscriptionManager
|
self.subscriptionManager = subscriptionManager
|
||||||
self.jsonClass = jsonClass
|
self.jsonClass = jsonClass
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
|
self.queue = queue
|
||||||
HTTPServer.__init__(self, *args, **kwargs)
|
HTTPServer.__init__(self, *args, **kwargs)
|
||||||
|
|
100
service.py
100
service.py
|
@ -43,9 +43,11 @@ import player
|
||||||
import videonodes
|
import videonodes
|
||||||
import websocket_client as wsc
|
import websocket_client as wsc
|
||||||
import downloadutils
|
import downloadutils
|
||||||
|
import playlist
|
||||||
|
|
||||||
import PlexAPI
|
import PlexAPI
|
||||||
import PlexCompanion
|
import PlexCompanion
|
||||||
|
import PlexFunctions as PF
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -105,10 +107,14 @@ class Service():
|
||||||
|
|
||||||
# Clear video nodes properties
|
# Clear video nodes properties
|
||||||
videonodes.VideoNodes().clearProperties()
|
videonodes.VideoNodes().clearProperties()
|
||||||
|
|
||||||
# Set the minimum database version
|
# Set the minimum database version
|
||||||
window('plex_minDBVersion', value="1.1.5")
|
window('plex_minDBVersion', value="1.1.5")
|
||||||
|
|
||||||
|
# Initialize playlist/queue stuff
|
||||||
|
self.queueId = None
|
||||||
|
self.playlist = None
|
||||||
|
|
||||||
def getLogLevel(self):
|
def getLogLevel(self):
|
||||||
try:
|
try:
|
||||||
logLevel = int(utils.settings('logLevel'))
|
logLevel = int(utils.settings('logLevel'))
|
||||||
|
@ -116,6 +122,79 @@ class Service():
|
||||||
logLevel = 0
|
logLevel = 0
|
||||||
return logLevel
|
return logLevel
|
||||||
|
|
||||||
|
def _getStartItem(self, string):
|
||||||
|
"""
|
||||||
|
Grabs the Plex id from e.g. '/library/metadata/12987'
|
||||||
|
|
||||||
|
and returns the tuple (typus, id) where typus is either 'queueId' or
|
||||||
|
'plexId' and id is the corresponding id as a string
|
||||||
|
"""
|
||||||
|
typus = 'plexId'
|
||||||
|
if string.startswith('/library/metadata'):
|
||||||
|
try:
|
||||||
|
string = string.split('/')[3]
|
||||||
|
except IndexError:
|
||||||
|
string = ''
|
||||||
|
else:
|
||||||
|
self.logMsg('Unknown string! %s' % string, -1)
|
||||||
|
return typus, string
|
||||||
|
|
||||||
|
def processTasks(self, task):
|
||||||
|
"""
|
||||||
|
Processes tasks picked up e.g. by Companion listener
|
||||||
|
|
||||||
|
task = {
|
||||||
|
'action': 'playlist'
|
||||||
|
'data': as received from Plex companion
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
self.logMsg('Processing: %s' % task, 2)
|
||||||
|
data = task['data']
|
||||||
|
|
||||||
|
if task['action'] == 'playlist':
|
||||||
|
try:
|
||||||
|
_, queueId, query = PF.ParseContainerKey(data['containerKey'])
|
||||||
|
except Exception as e:
|
||||||
|
self.logMsg('Exception while processing: %s' % e, -1)
|
||||||
|
import traceback
|
||||||
|
self.logMsg("Traceback:\n%s" % traceback.format_exc(), -1)
|
||||||
|
return
|
||||||
|
if self.playlist is not None:
|
||||||
|
if self.playlist.typus != data.get('type'):
|
||||||
|
self.logMsg('Switching to Kodi playlist of type %s'
|
||||||
|
% data.get('type'), 1)
|
||||||
|
self.playlist = None
|
||||||
|
self.queueId = None
|
||||||
|
if self.playlist is None:
|
||||||
|
if data.get('type') == 'music':
|
||||||
|
self.playlist = playlist.Playlist('music')
|
||||||
|
elif data.get('type') == 'video':
|
||||||
|
self.playlist = playlist.Playlist('video')
|
||||||
|
else:
|
||||||
|
self.playlist = playlist.Playlist()
|
||||||
|
if queueId != self.queueId:
|
||||||
|
self.logMsg('New playlist received, updating!', 1)
|
||||||
|
self.queueId = queueId
|
||||||
|
xml = PF.GetPlayQueue(queueId)
|
||||||
|
if xml in (None, 401):
|
||||||
|
self.logMsg('Could not download Plex playlist.', -1)
|
||||||
|
return
|
||||||
|
# Clear existing playlist on the Kodi side
|
||||||
|
self.playlist.clear()
|
||||||
|
items = []
|
||||||
|
for item in xml:
|
||||||
|
items.append({
|
||||||
|
'queueId': item.get('playQueueItemID'),
|
||||||
|
'plexId': item.get('ratingKey'),
|
||||||
|
'kodiId': None
|
||||||
|
})
|
||||||
|
self.playlist.playAll(
|
||||||
|
items,
|
||||||
|
startitem=self._getStartItem(data.get('key', '')),
|
||||||
|
offset=PF.ConvertPlexToKodiTime(data.get('offset', 0)))
|
||||||
|
else:
|
||||||
|
self.logMsg('This has never happened before!', -1)
|
||||||
|
|
||||||
def ServiceEntryPoint(self):
|
def ServiceEntryPoint(self):
|
||||||
|
|
||||||
log = self.logMsg
|
log = self.logMsg
|
||||||
|
@ -132,6 +211,8 @@ class Service():
|
||||||
|
|
||||||
# Queue for background sync
|
# Queue for background sync
|
||||||
queue = Queue.Queue(maxsize=200)
|
queue = Queue.Queue(maxsize=200)
|
||||||
|
# Queue for PlexCompanion listener
|
||||||
|
companionQueue = Queue.Queue(maxsize=100)
|
||||||
|
|
||||||
connectMsg = True if utils.settings('connectMsg') == 'true' else False
|
connectMsg = True if utils.settings('connectMsg') == 'true' else False
|
||||||
|
|
||||||
|
@ -195,7 +276,15 @@ class Service():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log("Exception in Playback Monitor Service: %s" % e, 1)
|
log("Exception in Playback Monitor Service: %s" % e, 1)
|
||||||
pass
|
pass
|
||||||
|
try:
|
||||||
|
task = companionQueue.get(block=False)
|
||||||
|
except Queue.Empty:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
|
# Got instructions from Plex Companions, process them
|
||||||
|
self.processTasks(task)
|
||||||
|
companionQueue.task_done()
|
||||||
|
if not self.kodimonitor_running:
|
||||||
# Start up events
|
# Start up events
|
||||||
self.warn_auth = True
|
self.warn_auth = True
|
||||||
if connectMsg and self.welcome_msg:
|
if connectMsg and self.welcome_msg:
|
||||||
|
@ -208,8 +297,8 @@ class Service():
|
||||||
time=2000,
|
time=2000,
|
||||||
sound=False)
|
sound=False)
|
||||||
# Start monitoring kodi events
|
# Start monitoring kodi events
|
||||||
if not self.kodimonitor_running:
|
self.kodimonitor_running = True
|
||||||
self.kodimonitor_running = kodimonitor.KodiMonitor()
|
kodimonitor.KodiMonitor()
|
||||||
|
|
||||||
# Start the Websocket Client
|
# Start the Websocket Client
|
||||||
if not self.websocket_running:
|
if not self.websocket_running:
|
||||||
|
@ -222,7 +311,8 @@ class Service():
|
||||||
# Start the Plex Companion thread
|
# Start the Plex Companion thread
|
||||||
if not self.plexCompanion_running:
|
if not self.plexCompanion_running:
|
||||||
self.plexCompanion_running = True
|
self.plexCompanion_running = True
|
||||||
plexCompanion = PlexCompanion.PlexCompanion()
|
plexCompanion = PlexCompanion.PlexCompanion(
|
||||||
|
companionQueue)
|
||||||
plexCompanion.start()
|
plexCompanion.start()
|
||||||
else:
|
else:
|
||||||
if (user.currUser is None) and self.warn_auth:
|
if (user.currUser is None) and self.warn_auth:
|
||||||
|
@ -313,7 +403,7 @@ class Service():
|
||||||
# Abort was requested while waiting.
|
# Abort was requested while waiting.
|
||||||
break
|
break
|
||||||
|
|
||||||
if monitor.waitForAbort(1):
|
if monitor.waitForAbort(0.05):
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue