Redesign playlists

This commit is contained in:
tomkat83 2016-07-20 18:36:31 +02:00
parent 5d79bcf1c2
commit 8a58c885e9
6 changed files with 276 additions and 134 deletions

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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)

View file

@ -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