Complete redesign of playlist handling

This commit is contained in:
tomkat83 2016-08-11 22:11:00 +02:00
parent 8ef66884df
commit ff1c7f9db1
5 changed files with 115 additions and 56 deletions

View file

@ -3,7 +3,6 @@ import threading
import traceback import traceback
import socket import socket
import Queue import Queue
import threading
import xbmc import xbmc
@ -34,8 +33,7 @@ class PlexCompanion(threading.Thread):
% self.client.getClientDetails(), 2) % self.client.getClientDetails(), 2)
# Initialize playlist/queue stuff # Initialize playlist/queue stuff
self.queueId = None self.playlist = playlist.Playlist('video')
self.playlist = None
# kodi player instance # kodi player instance
self.player = player.Player() self.player = player.Player()
@ -80,11 +78,10 @@ class PlexCompanion(threading.Thread):
self.logMsg("Traceback:\n%s" % traceback.format_exc(), -1) self.logMsg("Traceback:\n%s" % traceback.format_exc(), -1)
return return
if self.playlist is not None: if self.playlist is not None:
if self.playlist.typus != data.get('type'): if self.playlist.Typus() != data.get('type'):
self.logMsg('Switching to Kodi playlist of type %s' self.logMsg('Switching to Kodi playlist of type %s'
% data.get('type'), 1) % data.get('type'), 1)
self.playlist = None self.playlist = None
self.queueId = None
if self.playlist is None: if self.playlist is None:
if data.get('type') == 'music': if data.get('type') == 'music':
self.playlist = playlist.Playlist('music') self.playlist = playlist.Playlist('music')
@ -92,26 +89,35 @@ class PlexCompanion(threading.Thread):
self.playlist = playlist.Playlist('video') self.playlist = playlist.Playlist('video')
else: else:
self.playlist = playlist.Playlist() self.playlist = playlist.Playlist()
if queueId != self.queueId: if self.playlist is None:
self.logMsg('Could not initialize playlist', -1)
return
if queueId != self.playlist.QueueId():
self.logMsg('New playlist received, updating!', 1) self.logMsg('New playlist received, updating!', 1)
self.queueId = queueId
xml = GetPlayQueue(queueId) xml = GetPlayQueue(queueId)
if xml in (None, 401): if xml in (None, 401):
self.logMsg('Could not download Plex playlist.', -1) self.logMsg('Could not download Plex playlist.', -1)
return return
# Clear existing playlist on the Kodi side # Clear existing playlist on the Kodi side
self.playlist.clear() self.playlist.clear()
# Set new values
self.playlist.QueueId(queueId)
self.playlist.PlayQueueVersion(int(
xml.attrib.get('playQueueVersion')))
self.playlist.Guid(xml.attrib.get('guid'))
items = [] items = []
for item in xml: for item in xml:
items.append({ items.append({
'queueId': item.get('playQueueItemID'), 'playQueueItemID': item.get('playQueueItemID'),
'plexId': item.get('ratingKey'), 'plexId': item.get('ratingKey'),
'kodiId': None 'kodiId': None})
})
self.playlist.playAll( self.playlist.playAll(
items, items,
startitem=self._getStartItem(data.get('key', '')), startitem=self._getStartItem(data.get('key', '')),
offset=ConvertPlexToKodiTime(data.get('offset', 0))) offset=ConvertPlexToKodiTime(data.get('offset', 0)))
self.logMsg('Initiated playlist no %s with version %s'
% (self.playlist.QueueId(),
self.playlist.PlayQueueVersion()))
else: else:
self.logMsg('This has never happened before!', -1) self.logMsg('This has never happened before!', -1)
@ -127,7 +133,7 @@ class PlexCompanion(threading.Thread):
requestMgr = httppersist.RequestMgr() requestMgr = httppersist.RequestMgr()
jsonClass = functions.jsonClass(requestMgr, self.settings) jsonClass = functions.jsonClass(requestMgr, self.settings)
subscriptionManager = subscribers.SubscriptionManager( subscriptionManager = subscribers.SubscriptionManager(
jsonClass, requestMgr, self.player) jsonClass, requestMgr, self.player, self.playlist)
queue = Queue.Queue(maxsize=100) queue = Queue.Queue(maxsize=100)

View file

@ -278,11 +278,6 @@ class PlaybackUtils():
window('%s.itemid' % embyitem, value=itemid) window('%s.itemid' % embyitem, value=itemid)
window('%s.playcount' % embyitem, value=str(userdata['PlayCount'])) window('%s.playcount' % embyitem, value=str(userdata['PlayCount']))
# We need to keep track of playQueueItemIDs for Plex Companion
window('plex_%s.playQueueItemID'
% playurl, self.API.GetPlayQueueItemID())
window('plex_%s.guid' % playurl, self.API.getGuid())
if itemtype == "episode": if itemtype == "episode":
window('%s.refreshid' % embyitem, window('%s.refreshid' % embyitem,
value=self.API.getParentRatingKey()) value=self.API.getParentRatingKey())

View file

@ -232,9 +232,6 @@ class Player(xbmc.Player):
self.logMsg('Could not get kodi runtime, setting to zero', -1) self.logMsg('Could not get kodi runtime, setting to zero', -1)
runtime = 0 runtime = 0
playQueueVersion = window('playQueueVersion')
playQueueID = window('playQueueID')
playQueueItemID = window('plex_%s.playQueueItemID' % currentFile)
with embydb.GetEmbyDB() as emby_db: with embydb.GetEmbyDB() as emby_db:
emby_dbitem = emby_db.getItem_byId(itemId) emby_dbitem = emby_db.getItem_byId(itemId)
try: try:
@ -244,9 +241,6 @@ class Player(xbmc.Player):
fileid = None fileid = None
# Save data map for updates and position calls # Save data map for updates and position calls
data = { data = {
'playQueueVersion': playQueueVersion,
'playQueueID': playQueueID,
'playQueueItemID': playQueueItemID,
'runtime': runtime, 'runtime': runtime,
'item_id': itemId, 'item_id': itemId,
'refresh_id': refresh_id, 'refresh_id': refresh_id,
@ -415,9 +409,7 @@ class Player(xbmc.Player):
'emby_%s.type' % filename, 'emby_%s.type' % filename,
'emby_%s.runtime' % filename, 'emby_%s.runtime' % filename,
'emby_%s.playcount' % filename, 'emby_%s.playcount' % filename,
'plex_%s.playQueueItemID' % filename, 'plex_%s.playlistPosition' % filename
'plex_%s.playlistPosition' % filename,
'plex_%s.guid' % filename
) )
for item in cleanup: for item in cleanup:
utils.window(item, clear=True) utils.window(item, clear=True)

View file

@ -45,6 +45,15 @@ class Playlist():
# Borg - multiple instances, shared state # Borg - multiple instances, shared state
_shared_state = {} _shared_state = {}
typus = None
queueId = None
playQueueVersion = None
guid = None
playlistId = None
player = xbmc.Player()
# "interal" PKC playlist
items = []
@lockMethod.decorate @lockMethod.decorate
def __init__(self, typus=None): def __init__(self, typus=None):
# Borg # Borg
@ -53,6 +62,8 @@ class Playlist():
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 # Construct the Kodi playlist instance
if self.typus == typus:
return
if typus == 'video': if typus == 'video':
self.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) self.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
self.typus = 'video' self.typus = 'video'
@ -64,20 +75,53 @@ class Playlist():
else: else:
self.playlist = None self.playlist = None
self.typus = None self.typus = None
self.logMsg('Empty playlist initiated', 1)
if self.playlist is not None: if self.playlist is not None:
self.playlistId = self.playlist.getPlayListId() self.playlistId = self.playlist.getPlayListId()
self.player = xbmc.Player()
# "interal" PKC playlist @lockMethod.decorate
self.items = [] def getQueueIdFromPosition(self, playlistPosition):
return self.items[playlistPosition]['playQueueItemID']
@lockMethod.decorate
def Typus(self, value=None):
if value:
self.typus = value
else:
return self.typus
@lockMethod.decorate
def PlayQueueVersion(self, value=None):
if value:
self.playQueueVersion = value
else:
return self.playQueueVersion
@lockMethod.decorate
def QueueId(self, value=None):
if value:
self.queueId = value
else:
return self.queueId
@lockMethod.decorate
def Guid(self, value=None):
if value:
self.guid = value
else:
return self.guid
@lockMethod.decorate @lockMethod.decorate
def clear(self): def clear(self):
""" """
Empties current Kodi playlist and internal self.items list Empties current Kodi playlist and associated variables
""" """
self.logMsg('Clearing playlist', 1) self.logMsg('Clearing playlist', 1)
self.playlist.clear() self.playlist.clear()
self.items = [] self.items = []
self.queueId = None
self.playQueueVersion = None
self.guid = None
def _initiatePlaylist(self): def _initiatePlaylist(self):
self.logMsg('Initiating playlist', 1) self.logMsg('Initiating playlist', 1)
@ -161,13 +205,14 @@ class Playlist():
""" """
items: list of dicts of the form items: list of dicts of the form
{ {
'queueId': Plex playQueueItemID, e.g. '29175' 'playQueueItemID': Plex playQueueItemID, e.g. '29175'
'plexId': Plex ratingKey, e.g. '125' 'plexId': Plex ratingKey, e.g. '125'
'kodiId': Kodi's db id of the same item 'kodiId': Kodi's db id of the same item
} }
startitem: tuple (typus, id), where typus is either 'queueId' or startitem: tuple (typus, id), where typus is either
'plexId' and id is the corresponding id as a string 'playQueueItemID' or 'plexId' and id is the corresponding
id as a string
offset: First item's time offset to play in Kodi time (an int) offset: First item's time offset to play in Kodi time (an int)
""" """
self.logMsg("---*** PLAY ALL ***---", 1) self.logMsg("---*** PLAY ALL ***---", 1)
@ -201,13 +246,13 @@ class Playlist():
@lockMethod.decorate @lockMethod.decorate
def addtoPlaylist(self, dbid=None, mediatype=None, url=None): def addtoPlaylist(self, dbid=None, mediatype=None, url=None):
self._addtoPlaylist(dbid=None, mediatype=None, url=None)
def _addtoPlaylist(self, dbid=None, mediatype=None, url=None):
""" """
mediatype: Kodi type: 'movie', 'episode', 'musicvideo', 'artist', mediatype: Kodi type: 'movie', 'episode', 'musicvideo', 'artist',
'album', 'song', 'genre' 'album', 'song', 'genre'
""" """
self._addtoPlaylist(dbid=None, mediatype=None, url=None)
def _addtoPlaylist(self, dbid=None, mediatype=None, url=None):
pl = { pl = {
'jsonrpc': "2.0", 'jsonrpc': "2.0",
'id': 1, 'id': 1,

View file

@ -9,7 +9,7 @@ from functions import *
@logging @logging
class SubscriptionManager: class SubscriptionManager:
def __init__(self, jsonClass, RequestMgr, player): def __init__(self, jsonClass, RequestMgr, player, playlist):
self.serverlist = [] self.serverlist = []
self.subscribers = {} self.subscribers = {}
self.info = {} self.info = {}
@ -30,6 +30,7 @@ class SubscriptionManager:
self.playerprops = {} self.playerprops = {}
self.doUtils = downloadutils.DownloadUtils().downloadUrl self.doUtils = downloadutils.DownloadUtils().downloadUrl
self.xbmcplayer = player self.xbmcplayer = player
self.playlist = playlist
self.js = jsonClass self.js = jsonClass
self.RequestMgr = RequestMgr self.RequestMgr = RequestMgr
@ -111,6 +112,7 @@ class SubscriptionManager:
ret += ' playQueueVersion="%s"' % info.get('playQueueVersion') ret += ' playQueueVersion="%s"' % info.get('playQueueVersion')
ret += ' playQueueItemID="%s"' % info.get('playQueueItemID') ret += ' playQueueItemID="%s"' % info.get('playQueueItemID')
ret += ' containerKey="%s"' % self.containerKey ret += ' containerKey="%s"' % self.containerKey
ret += ' guid="%s"' % info['guid']
elif keyid: elif keyid:
self.containerKey = self.lastkey self.containerKey = self.lastkey
ret += ' containerKey="%s"' % self.containerKey ret += ' containerKey="%s"' % self.containerKey
@ -122,7 +124,6 @@ class SubscriptionManager:
ret += ' protocol="%s"' % serv.get('protocol', "http") ret += ' protocol="%s"' % serv.get('protocol', "http")
ret += ' address="%s"' % serv.get('server', self.server) ret += ' address="%s"' % serv.get('server', self.server)
ret += ' port="%s"' % serv.get('port', self.port) ret += ' port="%s"' % serv.get('port', self.port)
ret += ' guid="%s"' % info['guid']
ret += ' volume="%s"' % info['volume'] ret += ' volume="%s"' % info['volume']
ret += ' shuffle="%s"' % info['shuffle'] ret += ' shuffle="%s"' % info['shuffle']
ret += ' mute="%s"' % self.mute ret += ' mute="%s"' % self.mute
@ -221,30 +222,50 @@ class SubscriptionManager:
if sub.age > 30: if sub.age > 30:
sub.cleanup() sub.cleanup()
del self.subscribers[sub.uuid] del self.subscribers[sub.uuid]
def getPlayerProperties(self, playerid): def getPlayerProperties(self, playerid):
info = {}
try: try:
# get info from the player # get info from the player
props = self.js.jsonrpc("Player.GetProperties", {"playerid": playerid, "properties": ["time", "totaltime", "speed", "shuffled", "repeat"]}) props = self.js.jsonrpc(
self.logMsg(self.js.jsonrpc("Player.GetItem", {"playerid": playerid, "properties": ["file", "showlink", "episode", "season"]}), 2) "Player.GetProperties",
info['time'] = timeToMillis(props['time']) {"playerid": playerid,
info['duration'] = timeToMillis(props['totaltime']) "properties": ["time",
info['state'] = ("paused", "playing")[int(props['speed'])] "totaltime",
info['shuffle'] = ("0","1")[props.get('shuffled', False)] "speed",
info['repeat'] = pf.getPlexRepeat(props.get('repeat')) "shuffled",
# New PMS playQueue attributes "repeat"]})
cf = self.xbmcplayer.getPlayingFile()
info['playQueueID'] = window('playQueueID')
info['playQueueVersion'] = window('playQueueVersion')
info['playQueueItemID'] = window('plex_%s.playQueueItemID' % cf)
info['guid'] = window('plex_%s.guid' % cf)
info = {
'time': timeToMillis(props['time']),
'duration': timeToMillis(props['totaltime']),
'state': ("paused", "playing")[int(props['speed'])],
'shuffle': ("0", "1")[props.get('shuffled', False)],
'repeat': pf.getPlexRepeat(props.get('repeat')),
}
if self.playlist is not None:
if self.playlist.QueueId() is not None:
info['playQueueID'] = self.playlist.QueueId()
info['playQueueVersion'] = self.playlist.PlayQueueVersion()
info['guid'] = self.playlist.Guid()
# Get the playlist position
pos = self.js.jsonrpc(
"Player.GetProperties",
{"playerid": playerid,
"properties": ["position"]})
info['playQueueItemID'] = \
self.playlist.getQueueIdFromPosition(pos['position'])
except: except:
info['time'] = 0 import traceback
info['duration'] = 0 self.logMsg("Traceback:\n%s"
info['state'] = "stopped" % traceback.format_exc(), -1)
info['shuffle'] = False info = {
'time': 0,
'duration': 0,
'state': 'stopped',
'shuffle': False,
'repeat': 0
}
# get the volume from the application # get the volume from the application
info['volume'] = self.volume info['volume'] = self.volume
info['mute'] = self.mute info['mute'] = self.mute