Complete redesign of playlist handling
This commit is contained in:
parent
8ef66884df
commit
ff1c7f9db1
5 changed files with 115 additions and 56 deletions
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue