mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-12-27 03:06:10 +00:00
286 lines
No EOL
12 KiB
Python
286 lines
No EOL
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import json
|
|
import threading
|
|
import websocket
|
|
import logging
|
|
|
|
#################################################################################################
|
|
# WebSocket Client thread
|
|
#################################################################################################
|
|
|
|
import xbmc
|
|
import xbmcgui
|
|
import xbmcaddon
|
|
|
|
import KodiMonitor
|
|
import Utils as utils
|
|
from ClientInformation import ClientInformation
|
|
from DownloadUtils import DownloadUtils
|
|
from PlaybackUtils import PlaybackUtils
|
|
from LibrarySync import LibrarySync
|
|
from WriteKodiVideoDB import WriteKodiVideoDB
|
|
from WriteKodiMusicDB import WriteKodiMusicDB
|
|
|
|
logging.basicConfig()
|
|
|
|
|
|
class WebSocketThread(threading.Thread):
|
|
|
|
_shared_state = {}
|
|
|
|
doUtils = DownloadUtils()
|
|
clientInfo = ClientInformation()
|
|
KodiMonitor = KodiMonitor.Kodi_Monitor()
|
|
|
|
addonName = clientInfo.getAddonName()
|
|
|
|
client = None
|
|
keepRunning = True
|
|
|
|
def __init__(self, *args):
|
|
|
|
self.__dict__ = self._shared_state
|
|
threading.Thread.__init__(self, *args)
|
|
|
|
def logMsg(self, msg, lvl=1):
|
|
|
|
self.className = self.__class__.__name__
|
|
utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
|
|
|
|
def sendProgressUpdate(self, data):
|
|
self.logMsg("sendProgressUpdate", 1)
|
|
if self.client:
|
|
try:
|
|
# Send progress update
|
|
messageData = {
|
|
'MessageType': "ReportPlaybackProgress",
|
|
'Data': data
|
|
}
|
|
messageString = json.dumps(messageData)
|
|
self.client.send(messageString)
|
|
self.logMsg("Message data: %s" % messageString, 2)
|
|
except Exception as e:
|
|
self.logMsg("Exception: %s" % e, 1)
|
|
|
|
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:
|
|
self.logMsg("Stopping Client")
|
|
self.keepRunning = False
|
|
self.client.keep_running = False
|
|
self.client.close()
|
|
self.logMsg("Stopping Client : KeepRunning set to False")
|
|
else:
|
|
self.logMsg("Stopping Client NO Object ERROR")
|
|
|
|
def on_message(self, ws, message):
|
|
|
|
WINDOW = xbmcgui.Window(10000)
|
|
self.logMsg("Message: %s" % message, 1)
|
|
|
|
result = json.loads(message)
|
|
messageType = result['MessageType']
|
|
data = result.get("Data")
|
|
|
|
if messageType == "Play":
|
|
# A remote control play command has been sent from the server.
|
|
itemIds = data['ItemIds']
|
|
playCommand = data['PlayCommand']
|
|
|
|
if "PlayNow" in playCommand:
|
|
startPositionTicks = data.get('StartPositionTicks', 0)
|
|
xbmc.executebuiltin("Dialog.Close(all,true)")
|
|
xbmc.executebuiltin("XBMC.Notification(Playlist: Added %s items to Playlist,)" % len(itemIds))
|
|
PlaybackUtils().PLAYAllItems(itemIds, startPositionTicks)
|
|
|
|
elif "PlayNext" in playCommand:
|
|
xbmc.executebuiltin("XBMC.Notification(Playlist: Added %s items to Playlist,)" % len(itemIds))
|
|
playlist = PlaybackUtils().AddToPlaylist(itemIds)
|
|
if not xbmc.Player().isPlaying():
|
|
xbmc.Player().play(playlist)
|
|
|
|
elif messageType == "Playstate":
|
|
# A remote control update playstate command has been sent from the server.
|
|
command = data['Command']
|
|
|
|
if "Stop" in command:
|
|
self.logMsg("Playback Stopped.", 1)
|
|
xbmc.Player().stop()
|
|
elif "Unpause" in command:
|
|
self.logMsg("Playback unpaused.", 1)
|
|
xbmc.Player().pause()
|
|
elif "Pause" in command:
|
|
self.logMsg("Playback paused.", 1)
|
|
xbmc.Player().pause()
|
|
elif "NextTrack" in command:
|
|
self.logMsg("Playback next track.", 1)
|
|
xbmc.Player().playnext()
|
|
elif "PreviousTrack" in command:
|
|
self.logMsg("Playback previous track.", 1)
|
|
xbmc.Player().playprevious()
|
|
elif "Seek" in command:
|
|
seekPositionTicks = data['SeekPositionTicks']
|
|
seekTime = seekPositionTicks / 10000000.0
|
|
self.logMsg("Seek to %s" % seekTime, 1)
|
|
xbmc.Player().seekTime(seekTime)
|
|
# Report playback
|
|
WINDOW.setProperty('commandUpdate', "true")
|
|
|
|
elif messageType == "UserDataChanged":
|
|
# A user changed their personal rating for an item, or their playstate was updated
|
|
userdataList = data['UserDataList']
|
|
self.logMsg("Message: Doing UserDataChanged: UserDataList: %s" % userdataList, 1)
|
|
LibrarySync().user_data_update(userdataList)
|
|
|
|
elif messageType == "LibraryChanged":
|
|
# Library items
|
|
itemsRemoved = data.get("ItemsRemoved")
|
|
itemsAdded = data.get("ItemsAdded")
|
|
itemsUpdated = data.get("ItemsUpdated")
|
|
itemsToUpdate = itemsAdded + itemsUpdated
|
|
|
|
self.logMsg("Message: WebSocket LibraryChanged: Items Added: %s" % itemsAdded, 1)
|
|
self.logMsg("Message: WebSocket LibraryChanged: Items Updated: %s" % itemsUpdated, 1)
|
|
self.logMsg("Message: WebSocket LibraryChanged: Items Removed: %s" % itemsRemoved, 1)
|
|
|
|
LibrarySync().remove_items(itemsRemoved)
|
|
LibrarySync().update_items(itemsToUpdate)
|
|
|
|
elif messageType == "GeneralCommand":
|
|
|
|
command = data['Name']
|
|
arguments = data.get("Arguments")
|
|
|
|
if command in ('Mute', 'Unmute', 'SetVolume', 'SetSubtitleStreamIndex', 'SetAudioStreamIndex'):
|
|
# These commands need to be reported back
|
|
if command == "Mute":
|
|
xbmc.executebuiltin('Mute')
|
|
elif command == "Unmute":
|
|
xbmc.executebuiltin('Mute')
|
|
elif command == "SetVolume":
|
|
volume = arguments['Volume']
|
|
xbmc.executebuiltin('SetVolume(%s[,showvolumebar])' % volume)
|
|
elif command == "SetSubtitleStreamIndex":
|
|
# Emby merges audio and subtitle index together
|
|
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
|
index = int(arguments['Index']) - audioTracks
|
|
xbmc.Player().setSubtitleStream(index - 1)
|
|
elif command == "SetAudioStreamIndex":
|
|
index = int(arguments['Index'])
|
|
xbmc.Player().setAudioStream(index - 1)
|
|
# Report playback
|
|
WINDOW.setProperty('commandUpdate', "true")
|
|
|
|
else:
|
|
# GUI commands
|
|
if command == "ToggleFullscreen":
|
|
xbmc.executebuiltin('Action(FullScreen)')
|
|
elif command == "ToggleOsdMenu":
|
|
xbmc.executebuiltin('Action(OSD)')
|
|
elif command == "MoveUp":
|
|
xbmc.executebuiltin('Action(Up)')
|
|
elif command == "MoveDown":
|
|
xbmc.executebuiltin('Action(Down)')
|
|
elif command == "MoveLeft":
|
|
xbmc.executebuiltin('Action(Left)')
|
|
elif command == "MoveRight":
|
|
xbmc.executebuiltin('Action(Right)')
|
|
elif command == "Select":
|
|
xbmc.executebuiltin('Action(Select)')
|
|
elif command == "Back":
|
|
xbmc.executebuiltin('Action(back)')
|
|
elif command == "ToggleContextMenu":
|
|
xbmc.executebuiltin('Action(ContextMenu)')
|
|
elif command == "GoHome":
|
|
xbmc.executebuiltin('ActivateWindow(Home)')
|
|
elif command == "PageUp":
|
|
xbmc.executebuiltin('Action(PageUp)')
|
|
elif command == "NextLetter":
|
|
xbmc.executebuiltin('Action(NextLetter)')
|
|
elif command == "GoToSearch":
|
|
xbmc.executebuiltin('VideoLibrary.Search')
|
|
elif command == "GoToSettings":
|
|
xbmc.executebuiltin('ActivateWindow(Settings)')
|
|
elif command == "PageDown":
|
|
xbmc.executebuiltin('Action(PageDown)')
|
|
elif command == "PreviousLetter":
|
|
xbmc.executebuiltin('Action(PrevLetter)')
|
|
elif command == "TakeScreenshot":
|
|
xbmc.executebuiltin('TakeScreenshot')
|
|
elif command == "ToggleMute":
|
|
xbmc.executebuiltin('Mute')
|
|
elif command == "VolumeUp":
|
|
xbmc.executebuiltin('Action(VolumeUp)')
|
|
elif command == "VolumeDown":
|
|
xbmc.executebuiltin('Action(VolumeDown)')
|
|
elif command == "DisplayMessage":
|
|
header = arguments['Header']
|
|
text = arguments['Text']
|
|
xbmcgui.Dialog().notification(header, text, icon="special://home/addons/plugin.video.emby/icon.png", time=4000)
|
|
elif command == "SendString":
|
|
string = arguments['String']
|
|
text = '{"jsonrpc": "2.0", "method": "Input.SendText", "params": { "text": "%s", "done": false }, "id": 0}' % string
|
|
result = xbmc.executeJSONRPC(text)
|
|
else:
|
|
self.logMsg("Unknown command.", 1)
|
|
|
|
elif messageType == "ServerRestarting":
|
|
if utils.settings('supressRestartMsg') == "true":
|
|
xbmcgui.Dialog().notification("Emby server", "Server is restarting.", icon="special://home/addons/plugin.video.emby/icon.png")
|
|
|
|
def on_error(self, ws, error):
|
|
if "10061" in str(error):
|
|
# Server is offline
|
|
pass
|
|
else:
|
|
self.logMsg("Error: %s" % error, 1)
|
|
#raise
|
|
|
|
def on_close(self, ws):
|
|
self.logMsg("Closed", 2)
|
|
|
|
def on_open(self, ws):
|
|
deviceId = ClientInformation().getMachineId()
|
|
self.doUtils.postCapabilities(deviceId)
|
|
|
|
def run(self):
|
|
|
|
WINDOW = xbmcgui.Window(10000)
|
|
logLevel = int(WINDOW.getProperty('getLogLevel'))
|
|
username = WINDOW.getProperty('currUser')
|
|
server = WINDOW.getProperty('server%s' % username)
|
|
token = WINDOW.getProperty('accessToken%s' % username)
|
|
deviceId = ClientInformation().getMachineId()
|
|
|
|
'''if (logLevel == 2):
|
|
websocket.enableTrace(True)'''
|
|
|
|
# Get the appropriate prefix for websocket
|
|
if "https" in server:
|
|
server = server.replace('https', 'wss')
|
|
else:
|
|
server = server.replace('http', 'ws')
|
|
|
|
websocketUrl = "%s?api_key=%s&deviceId=%s" % (server, token, deviceId)
|
|
self.logMsg("websocket URL: %s" % 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.client.run_forever()
|
|
|
|
if self.keepRunning:
|
|
self.logMsg("Client Needs To Restart", 2)
|
|
if self.KodiMonitor.waitForAbort(5):
|
|
break
|
|
|
|
self.logMsg("Thread Exited", 1) |