add WebSocket for events and remote control

For some reason playback stop is not being reported
This commit is contained in:
faush01 2015-03-23 15:54:16 +11:00
parent 59cdfcd922
commit 9e5385c1c6
5 changed files with 398 additions and 2 deletions

View file

@ -55,7 +55,8 @@ class DownloadUtils():
host = addon.getSetting('ipaddress') host = addon.getSetting('ipaddress')
port = addon.getSetting('port') port = addon.getSetting('port')
server = host + ":" + port server = host + ":" + port
return server
'''
if len(server) < 2: if len(server) < 2:
self.logMsg("No server information saved.") self.logMsg("No server information saved.")
return "" return ""
@ -71,7 +72,7 @@ class DownloadUtils():
# If only the host:port is required # If only the host:port is required
elif (prefix == False): elif (prefix == False):
return server return server
'''
def getUserId(self, suppress=True): def getUserId(self, suppress=True):
WINDOW = xbmcgui.Window( 10000 ) WINDOW = xbmcgui.Window( 10000 )

View file

@ -203,3 +203,128 @@ class PlaybackUtils():
listItem.setInfo('video', {'writer' : people.get('Writer')}) listItem.setInfo('video', {'writer' : people.get('Writer')})
listItem.setInfo('video', {'mpaa': result.get("OfficialRating")}) listItem.setInfo('video', {'mpaa': result.get("OfficialRating")})
listItem.setInfo('video', {'genre': genre}) listItem.setInfo('video', {'genre': genre})
def seekToPosition(self, seekTo):
#Set a loop to wait for positive confirmation of playback
count = 0
while not xbmc.Player().isPlaying():
self.logMsg( "Not playing yet...sleep for 1 sec")
count = count + 1
if count >= 10:
return
else:
time.sleep(1)
#Jump to resume point
jumpBackSec = int(self.settings.getSetting("resumeJumpBack"))
seekToTime = seekTo - jumpBackSec
count = 0
while xbmc.Player().getTime() < (seekToTime - 5) and count < 11: # only try 10 times
count = count + 1
xbmc.Player().pause
xbmc.sleep(100)
xbmc.Player().seekTime(seekToTime)
xbmc.sleep(100)
xbmc.Player().play()
def PLAYAllItems(self, items, startPositionTicks):
xbmc.log("== ENTER: PLAYAllItems ==")
xbmc.log("Items : " + str(items))
du = DownloadUtils()
userid = du.getUserId()
server = du.getServer()
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
started = False
for itemID in items:
xbmc.log("Adding Item to Playlist : " + itemID)
item_url = "http://" + server + "/mediabrowser/Users/" + userid + "/Items/" + itemID + "?format=json"
jsonData = du.downloadUrl(item_url, suppress=False, popup=1 )
item_data = json.loads(jsonData)
added = self.addPlaylistItem(playlist, item_data, server, userid)
if(added and started == False):
started = True
xbmc.log("Starting Playback Pre")
xbmc.Player().play(playlist)
if(started == False):
xbmc.log("Starting Playback Post")
xbmc.Player().play(playlist)
#seek to position
seekTime = 0
if(startPositionTicks != None):
seekTime = (startPositionTicks / 1000) / 10000
if seekTime > 0:
self.seekToPosition(seekTime)
def AddToPlaylist(self, itemIds):
xbmc.log("== ENTER: PLAYAllItems ==")
du = DownloadUtils()
userid = du.getUserId()
server = du.getServer()
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
for itemID in itemIds:
xbmc.log("Adding Item to Playlist : " + itemID)
item_url = "http://" + server + "/mediabrowser/Users/" + userid + "/Items/" + itemID + "?format=json"
jsonData = du.downloadUrl(item_url, suppress=False, popup=1 )
item_data = json.loads(jsonData)
self.addPlaylistItem(playlist, item_data, server, userid)
return playlist
def addPlaylistItem(self, playlist, item, server, userid):
id = item.get("Id")
playurl = PlayUtils().getPlayUrl(server, id, item)
xbmc.log("Play URL: " + playurl)
api = API()
thumbPath = api.getArtwork(item, "Primary")
listItem = xbmcgui.ListItem(path=playurl, iconImage=thumbPath, thumbnailImage=thumbPath)
self.setListItemProps(server, id, listItem, item)
# Can not play virtual items
if (item.get("LocationType") == "Virtual") or (item.get("IsPlaceHolder") == True):
xbmcgui.Dialog().ok(self.language(30128), self.language(30129))
return False
else:
watchedurl = 'http://' + server + '/mediabrowser/Users/'+ userid + '/PlayedItems/' + id
positionurl = 'http://' + server + '/mediabrowser/Users/'+ userid + '/PlayingItems/' + id
# set the current playing info
WINDOW = xbmcgui.Window( 10000 )
WINDOW.setProperty(playurl + "watchedurl", watchedurl)
WINDOW.setProperty(playurl + "positionurl", positionurl)
WINDOW.setProperty(playurl + "runtimeticks", str(item.get("RunTimeTicks")))
WINDOW.setProperty(playurl+"type", item.get("Type"))
WINDOW.setProperty(playurl + "item_id", id)
if (item.get("Type") == "Episode"):
WINDOW.setProperty(playurl + "refresh_id", item.get("SeriesId"))
else:
WINDOW.setProperty(playurl + "refresh_id", id)
xbmc.log( "PlayList Item Url : " + str(playurl))
playlist.add(playurl, listItem)
return True

View file

@ -0,0 +1,262 @@
#################################################################################################
# WebSocket Client thread
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
import urllib
import socket
import websocket
from ClientInformation import ClientInformation
from DownloadUtils import DownloadUtils
from PlaybackUtils import PlaybackUtils
_MODE_BASICPLAY=12
class WebSocketThread(threading.Thread):
logLevel = 0
client = None
keepRunning = True
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("MB3SYNC WebSocketThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
try:
xbmc.log("MB3SYNC WebSocketThread -> " + str(msg))
except UnicodeEncodeError:
try:
xbmc.log("MB3SYNC WebSocketThread -> " + str(msg.encode('utf-8')))
except: pass
'''
def playbackStarted(self, itemId):
if(self.client != None):
try:
self.logMsg("Sending Playback Started")
messageData = {}
messageData["MessageType"] = "PlaybackStart"
messageData["Data"] = itemId + "|true|audio,video"
messageString = json.dumps(messageData)
self.logMsg("Message Data : " + messageString)
self.client.send(messageString)
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
else:
self.logMsg("Sending Playback Started NO Object ERROR")
'''
'''
def playbackStopped(self, itemId, ticks):
if(self.client != None):
try:
self.logMsg("Sending Playback Stopped")
messageData = {}
messageData["MessageType"] = "PlaybackStopped"
messageData["Data"] = itemId + "|" + str(ticks)
messageString = json.dumps(messageData)
self.client.send(messageString)
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
else:
self.logMsg("Sending Playback Stopped NO Object ERROR")
'''
'''
def sendProgressUpdate(self, itemId, ticks):
if(self.client != None):
try:
self.logMsg("Sending Progress Update")
messageData = {}
messageData["MessageType"] = "PlaybackProgress"
messageData["Data"] = itemId + "|" + str(ticks) + "|false|false"
messageString = json.dumps(messageData)
self.logMsg("Message Data : " + messageString)
self.client.send(messageString)
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
else:
self.logMsg("Sending Progress Update NO Object ERROR")
'''
def stopClient(self):
# stopping the client is tricky, first set keep_running to false and then trigger one
# more message by requesting one SessionsStart message, this causes the
# client to receive the message and then exit
if(self.client != None):
self.logMsg("Stopping Client")
self.keepRunning = False
self.client.keep_running = False
self.client.close()
self.logMsg("Stopping Client : KeepRunning set to False")
'''
try:
self.keepRunning = False
self.client.keep_running = False
self.logMsg("Stopping Client")
self.logMsg("Calling Ping")
self.client.sock.ping()
self.logMsg("Calling Socket Shutdown()")
self.client.sock.sock.shutdown(socket.SHUT_RDWR)
self.logMsg("Calling Socket Close()")
self.client.sock.sock.close()
self.logMsg("Stopping Client Done")
self.logMsg("Calling Ping")
self.client.sock.ping()
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
'''
else:
self.logMsg("Stopping Client NO Object ERROR")
def on_message(self, ws, message):
self.logMsg("Message : " + str(message))
result = json.loads(message)
messageType = result.get("MessageType")
playCommand = result.get("PlayCommand")
data = result.get("Data")
if(messageType != None and messageType == "Play" and data != None):
itemIds = data.get("ItemIds")
playCommand = data.get("PlayCommand")
if(playCommand != None and playCommand == "PlayNow"):
xbmc.executebuiltin("Dialog.Close(all,true)")
startPositionTicks = data.get("StartPositionTicks")
PlaybackUtils().PLAYAllItems(itemIds, startPositionTicks)
xbmc.executebuiltin("XBMC.Notification(Playlist: Added " + str(len(itemIds)) + " items to Playlist,)")
elif(playCommand != None and playCommand == "PlayNext"):
playlist = PlaybackUtils().AddToPlaylist(itemIds)
xbmc.executebuiltin("XBMC.Notification(Playlist: Added " + str(len(itemIds)) + " items to Playlist,)")
if(xbmc.Player().isPlaying() == False):
xbmc.Player().play(playlist)
elif(messageType != None and messageType == "Playstate"):
command = data.get("Command")
if(command != None and command == "Stop"):
self.logMsg("Playback Stopped")
xbmc.executebuiltin('xbmc.activatewindow(10000)')
xbmc.Player().stop()
elif(command != None and command == "Pause"):
self.logMsg("Playback Paused")
xbmc.Player().pause()
elif(command != None and command == "Unpause"):
self.logMsg("Playback UnPaused")
xbmc.Player().pause()
elif(command != None and command == "NextTrack"):
self.logMsg("Playback NextTrack")
xbmc.Player().playnext()
elif(command != None and command == "PreviousTrack"):
self.logMsg("Playback PreviousTrack")
xbmc.Player().playprevious()
elif(command != None and command == "Seek"):
seekPositionTicks = data.get("SeekPositionTicks")
self.logMsg("Playback Seek : " + str(seekPositionTicks))
seekTime = (seekPositionTicks / 1000) / 10000
xbmc.Player().seekTime(seekTime)
def on_error(self, ws, error):
self.logMsg("Error : " + str(error))
#raise
def on_close(self, ws):
self.logMsg("Closed")
def on_open(self, ws):
clientInfo = ClientInformation()
machineId = clientInfo.getMachineId()
version = clientInfo.getVersion()
messageData = {}
messageData["MessageType"] = "Identity"
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
deviceName = addonSettings.getSetting('deviceName')
deviceName = deviceName.replace("\"", "_")
messageData["Data"] = "Kodi|" + machineId + "|" + version + "|" + deviceName
messageString = json.dumps(messageData)
self.logMsg("Opened : " + str(messageString))
ws.send(messageString)
# Set Capabilities
xbmc.log("postcapabilities_called")
downloadUtils = DownloadUtils()
downloadUtils.postcapabilities()
def getWebSocketPort(self, host, port):
userUrl = "http://" + host + ":" + port + "/mediabrowser/System/Info?format=json"
downloadUtils = DownloadUtils()
jsonData = downloadUtils.downloadUrl(userUrl, suppress=True, popup=1 )
if(jsonData == ""):
return -1
result = json.loads(jsonData)
wsPort = result.get("WebSocketPortNumber")
if(wsPort != None):
return wsPort
else:
return -1
def run(self):
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
if(self.logLevel >= 1):
websocket.enableTrace(True)
'''
wsPort = self.getWebSocketPort(mb3Host, mb3Port);
self.logMsg("WebSocketPortNumber = " + str(wsPort))
if(wsPort == -1):
self.logMsg("Could not retrieve WebSocket port, can not run WebScoket Client")
return
'''
# Make a call to /System/Info. WebSocketPortNumber is the port hosting the web socket.
webSocketUrl = "ws://" + mb3Host + ":" + mb3Port + "/mediabrowser"
self.logMsg("WebSocket URL : " + webSocketUrl)
self.client = websocket.WebSocketApp(webSocketUrl,
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close)
self.client.on_open = self.on_open
while(self.keepRunning):
self.logMsg("Client Starting")
self.client.run_forever()
if(self.keepRunning):
self.logMsg("Client Needs To Restart")
xbmc.sleep(10000)
self.logMsg("Thread Exited")

View file

@ -20,6 +20,8 @@
<setting id="port" type="text" label="30030" default="8096" visible="true" enable="true" /> <setting id="port" type="text" label="30030" default="8096" visible="true" enable="true" />
<setting type="sep" /> <setting type="sep" />
<setting id="username" type="text" label="30024" /> <setting id="username" type="text" label="30024" />
<setting type="sep" />
<setting id="deviceName" type="text" label="30024" default="Kodi"/>
</category> </category>
<category label="30022"> <!-- Advanced --> <category label="30022"> <!-- Advanced -->

View file

@ -17,6 +17,7 @@ from LibrarySync import LibrarySync
from Player import Player from Player import Player
from DownloadUtils import DownloadUtils from DownloadUtils import DownloadUtils
from ConnectionManager import ConnectionManager from ConnectionManager import ConnectionManager
from WebSocketClient import WebSocketThread
librarySync = LibrarySync() librarySync = LibrarySync()
class Service(): class Service():
@ -37,6 +38,8 @@ class Service():
player = Player() player = Player()
lastProgressUpdate = datetime.today() lastProgressUpdate = datetime.today()
newWebSocketThread = WebSocketThread()
newWebSocketThread.start()
# check kodi library sources # check kodi library sources
mayRun = utils.checkKodiSources() mayRun = utils.checkKodiSources()
@ -116,6 +119,9 @@ class Service():
xbmc.log("Not authenticated yet") xbmc.log("Not authenticated yet")
utils.logMsg("MB3 Sync Service", "stopping Service",0) utils.logMsg("MB3 Sync Service", "stopping Service",0)
if(newWebSocketThread != None):
newWebSocketThread.stopClient()
#start the service #start the service