add WebSocket for events and remote control
For some reason playback stop is not being reported
This commit is contained in:
parent
59cdfcd922
commit
9e5385c1c6
5 changed files with 398 additions and 2 deletions
|
@ -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 )
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
262
resources/lib/WebSocketClient.py
Normal file
262
resources/lib/WebSocketClient.py
Normal 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")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue