diff --git a/resources/lib/player.py b/resources/lib/player.py index 4fa2608d..ba512c18 100644 --- a/resources/lib/player.py +++ b/resources/lib/player.py @@ -368,7 +368,7 @@ class Player(xbmc.Player): # Report progress via websocketclient postdata = json.dumps(postdata) log.debug("Report: %s" % postdata) - self.ws.sendProgressUpdate(postdata) + self.ws.send_progress_update(postdata) def onPlayBackPaused(self): diff --git a/resources/lib/utils.py b/resources/lib/utils.py index ef84110c..ba226d61 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -55,7 +55,7 @@ def dialog(type_, **kwargs): d = xbmcgui.Dialog() if "icon" in kwargs: - kwargs['icon'] = kwargs['icon'].replace("{default}", + kwargs['icon'] = kwargs['icon'].replace("{emby}", "special://home/addons/plugin.video.emby/icon.png") types = { diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 2f5e72bf..eba226d2 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -8,14 +8,13 @@ import threading import websocket import xbmc -import xbmcgui import clientinfo import downloadutils import librarysync import playlist import userclient -from utils import window, settings, language as lang, JSONRPC +from utils import window, settings, dialog, language as lang, JSONRPC ################################################################################################## @@ -28,8 +27,8 @@ class WebSocket_Client(threading.Thread): _shared_state = {} - client = None - stopWebsocket = False + _client = None + _stop_websocket = False def __init__(self): @@ -37,103 +36,56 @@ class WebSocket_Client(threading.Thread): self.__dict__ = self._shared_state self.monitor = xbmc.Monitor() - self.doUtils = downloadutils.DownloadUtils() - self.clientInfo = clientinfo.ClientInfo() - self.deviceId = self.clientInfo.get_device_id() - self.librarySync = librarysync.LibrarySync() + self.doutils = downloadutils.DownloadUtils() + self.client_info = clientinfo.ClientInfo() + self.device_id = self.client_info.get_device_id() + self.library_sync = librarysync.LibrarySync() threading.Thread.__init__(self) - def sendProgressUpdate(self, data): - + def send_progress_update(self, data): + log.debug("sendProgressUpdate") try: - messageData = { + message = { 'MessageType': "ReportPlaybackProgress", 'Data': data } - messageString = json.dumps(messageData) - self.client.send(messageString) - log.debug("Message data: %s" % messageString) + message_str = json.dumps(message) + self._client.send(message_str) + log.debug("Message data: %s", message_str) - except Exception as e: - log.exception(e) + except Exception as error: + log.exception(error) def on_message(self, ws, message): result = json.loads(message) - messageType = result['MessageType'] + message_type = result['MessageType'] data = result['Data'] - dialog = xbmcgui.Dialog() - if messageType not in ('SessionEnded'): + if message_type not in ('SessionEnded'): # Mute certain events log.info("Message: %s" % message) - if messageType == "Play": + if message_type == "Play": # A remote control play command has been sent from the server. - itemIds = data['ItemIds'] - command = data['PlayCommand'] + self._play_(data) - pl = playlist.Playlist() - - if command == "PlayNow": - dialog.notification( - heading=lang(29999), - message="%s %s" % (len(itemIds), lang(33004)), - icon="special://home/addons/plugin.video.emby/icon.png", - sound=False) - startat = data.get('StartPositionTicks', 0) - pl.playAll(itemIds, startat) - - elif command == "PlayNext": - dialog.notification( - heading=lang(29999), - message="%s %s" % (len(itemIds), lang(33005)), - icon="special://home/addons/plugin.video.emby/icon.png", - sound=False) - newplaylist = pl.modifyPlaylist(itemIds) - player = xbmc.Player() - if not player.isPlaying(): - # Only start the playlist if nothing is playing - player.play(newplaylist) - - elif messageType == "Playstate": + elif message_type == "Playstate": # A remote control update playstate command has been sent from the server. - command = data['Command'] - player = xbmc.Player() + self._playstate_(data) - actions = { - - 'Stop': player.stop, - 'Unpause': player.pause, - 'Pause': player.pause, - 'NextTrack': player.playnext, - 'PreviousTrack': player.playprevious, - 'Seek': player.seekTime - } - action = actions[command] - if command == "Seek": - seekto = data['SeekPositionTicks'] - seektime = seekto / 10000000.0 - action(seektime) - log.info("Seek to %s." % seektime) - else: - action() - log.info("Command: %s completed." % command) - - window('emby_command', value="true") - - elif messageType == "UserDataChanged": + elif message_type == "UserDataChanged": # A user changed their personal rating for an item, or their playstate was updated userdata_list = data['UserDataList'] - self.librarySync.triage_items("userdata", userdata_list) + self.library_sync.triage_items("userdata", userdata_list) - elif messageType == "LibraryChanged": + elif message_type == "LibraryChanged": - librarySync = self.librarySync + librarySync = self.library_sync processlist = { 'added': data['ItemsAdded'], @@ -143,163 +95,226 @@ class WebSocket_Client(threading.Thread): for action in processlist: librarySync.triage_items(action, processlist[action]) - elif messageType == "GeneralCommand": - - command = data['Name'] - arguments = data['Arguments'] + elif message_type == "GeneralCommand": + self._general_commands(data) - if command in ('Mute', 'Unmute', 'SetVolume', - 'SetSubtitleStreamIndex', 'SetAudioStreamIndex'): + elif message_type == "ServerRestarting": + self._server_restarting() - player = xbmc.Player() - # 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 == "SetAudioStreamIndex": - index = int(arguments['Index']) - player.setAudioStream(index - 1) - elif command == "SetSubtitleStreamIndex": - embyindex = int(arguments['Index']) - currentFile = player.getPlayingFile() - - mapping = window('emby_%s.indexMapping' % currentFile) - if mapping: - externalIndex = json.loads(mapping) - # If there's external subtitles added via playbackutils - for index in externalIndex: - if externalIndex[index] == embyindex: - player.setSubtitleStream(int(index)) - break - else: - # User selected internal subtitles - external = len(externalIndex) - audioTracks = len(player.getAvailableAudioStreams()) - player.setSubtitleStream(external + embyindex - audioTracks - 1) - else: - # Emby merges audio and subtitle index together - audioTracks = len(player.getAvailableAudioStreams()) - player.setSubtitleStream(index - audioTracks - 1) - - # Let service know - window('emby_command', value="true") - - elif command == "DisplayMessage": - - header = arguments['Header'] - text = arguments['Text'] - dialog.notification( - heading=header, - message=text, - icon="special://home/addons/plugin.video.emby/icon.png", - time=4000) - - elif command == "SendString": - - params = { - 'text': arguments['String'], - 'done': False - } - result = JSONRPC("Input.SendText").execute(params) - - elif command in ("MoveUp", "MoveDown", "MoveRight", "MoveLeft"): - # Commands that should wake up display - actions = { - 'MoveUp': "Input.Up", - 'MoveDown': "Input.Down", - 'MoveRight': "Input.Right", - 'MoveLeft': "Input.Left" - } - result = JSONRPC(actions[command]).execute() - - elif command == "GoHome": - result = JSONRPC("GUI.ActivateWindow").execute({"window":"home"}) - - else: - builtin = { - - 'ToggleFullscreen': 'Action(FullScreen)', - 'ToggleOsdMenu': 'Action(OSD)', - 'ToggleContextMenu': 'Action(ContextMenu)', - 'Select': 'Action(Select)', - 'Back': 'Action(back)', - 'PageUp': 'Action(PageUp)', - 'NextLetter': 'Action(NextLetter)', - 'GoToSearch': 'VideoLibrary.Search', - 'GoToSettings': 'ActivateWindow(Settings)', - 'PageDown': 'Action(PageDown)', - 'PreviousLetter': 'Action(PrevLetter)', - 'TakeScreenshot': 'TakeScreenshot', - 'ToggleMute': 'Mute', - 'VolumeUp': 'Action(VolumeUp)', - 'VolumeDown': 'Action(VolumeDown)', - } - action = builtin.get(command) - if action: - xbmc.executebuiltin(action) - - elif messageType == "ServerRestarting": - if settings('supressRestartMsg') == "true": - dialog.notification( - heading=lang(29999), - message=lang(33006), - icon="special://home/addons/plugin.video.emby/icon.png") - - elif messageType == "UserConfigurationUpdated": + elif message_type == "UserConfigurationUpdated": # Update user data set in userclient userclient.UserClient().get_user(data) - self.librarySync.refresh_views = True + self.library_sync.refresh_views = True - elif messageType == "ServerShuttingDown": + elif message_type == "ServerShuttingDown": # Server went offline window('emby_online', value="false") + @classmethod + def _play_(cls, data): + + item_ids = data['ItemIds'] + command = data['PlayCommand'] + + playlist_ = playlist.Playlist() + + if command == "PlayNow": + startat = data.get('StartPositionTicks', 0) + playlist_.playAll(item_ids, startat) + dialog(type_="notification", + heading=lang(29999), + message="%s %s" % (len(item_ids), lang(33004)), + icon="{emby}", + sound=False) + + elif command == "PlayNext": + newplaylist = playlist_.modifyPlaylist(item_ids) + dialog(type_="notification", + heading=lang(29999), + message="%s %s" % (len(item_ids), lang(33005)), + icon="{emby}", + sound=False) + player = xbmc.Player() + if not player.isPlaying(): + # Only start the playlist if nothing is playing + player.play(newplaylist) + + @classmethod + def _playstate_(cls, data): + + command = data['Command'] + player = xbmc.Player() + + actions = { + + 'Stop': player.stop, + 'Unpause': player.pause, + 'Pause': player.pause, + 'NextTrack': player.playnext, + 'PreviousTrack': player.playprevious, + 'Seek': player.seekTime + } + action = actions[command] + if command == "Seek": + seekto = data['SeekPositionTicks'] + seektime = seekto / 10000000.0 + action(seektime) + log.info("Seek to %s", seektime) + else: + action() + log.info("Command: %s completed", command) + + window('emby_command', value="true") + + @classmethod + def _general_commands(cls, data): + + command = data['Name'] + arguments = data['Arguments'] + + if command in ('Mute', 'Unmute', 'SetVolume', + 'SetSubtitleStreamIndex', 'SetAudioStreamIndex'): + + player = xbmc.Player() + # 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 == "SetAudioStreamIndex": + index = int(arguments['Index']) + player.setAudioStream(index - 1) + elif command == "SetSubtitleStreamIndex": + embyindex = int(arguments['Index']) + currentFile = player.getPlayingFile() + + mapping = window('emby_%s.indexMapping' % currentFile) + if mapping: + externalIndex = json.loads(mapping) + # If there's external subtitles added via playbackutils + for index in externalIndex: + if externalIndex[index] == embyindex: + player.setSubtitleStream(int(index)) + break + else: + # User selected internal subtitles + external = len(externalIndex) + audioTracks = len(player.getAvailableAudioStreams()) + player.setSubtitleStream(external + embyindex - audioTracks - 1) + else: + # Emby merges audio and subtitle index together + audioTracks = len(player.getAvailableAudioStreams()) + player.setSubtitleStream(index - audioTracks - 1) + + # Let service know + window('emby_command', value="true") + + elif command == "DisplayMessage": + + header = arguments['Header'] + text = arguments['Text'] + dialog(type_="notification", + heading=header, + message=text, + icon="{emby}", + time=4000) + + elif command == "SendString": + + params = { + 'text': arguments['String'], + 'done': False + } + result = JSONRPC("Input.SendText").execute(params) + + elif command in ("MoveUp", "MoveDown", "MoveRight", "MoveLeft"): + # Commands that should wake up display + actions = { + 'MoveUp': "Input.Up", + 'MoveDown': "Input.Down", + 'MoveRight': "Input.Right", + 'MoveLeft': "Input.Left" + } + result = JSONRPC(actions[command]).execute() + + elif command == "GoHome": + result = JSONRPC("GUI.ActivateWindow").execute({"window":"home"}) + + else: + builtin = { + + 'ToggleFullscreen': 'Action(FullScreen)', + 'ToggleOsdMenu': 'Action(OSD)', + 'ToggleContextMenu': 'Action(ContextMenu)', + 'Select': 'Action(Select)', + 'Back': 'Action(back)', + 'PageUp': 'Action(PageUp)', + 'NextLetter': 'Action(NextLetter)', + 'GoToSearch': 'VideoLibrary.Search', + 'GoToSettings': 'ActivateWindow(Settings)', + 'PageDown': 'Action(PageDown)', + 'PreviousLetter': 'Action(PrevLetter)', + 'TakeScreenshot': 'TakeScreenshot', + 'ToggleMute': 'Mute', + 'VolumeUp': 'Action(VolumeUp)', + 'VolumeDown': 'Action(VolumeDown)', + } + action = builtin.get(command) + if action: + xbmc.executebuiltin(action) + + @classmethod + def _server_restarting(cls): + + if settings('supressRestartMsg') == "true": + dialog(type_="notification", + heading=lang(29999), + message=lang(33006), + icon="{emby}") + def on_close(self, ws): - log.debug("Closed.") + log.debug("closed") def on_open(self, ws): - self.doUtils.postCapabilities(self.deviceId) + self.doutils.postCapabilities(self.device_id) def on_error(self, ws, error): if "10061" in str(error): # Server is offline pass else: - log.debug("Error: %s" % error) + log.debug("Error: %s", error) def run(self): - loglevel = int(window('emby_logLevel')) # websocket.enableTrace(True) - userId = window('emby_currUser') - server = window('emby_server%s' % userId) - token = window('emby_accessToken%s' % userId) + user_id = window('emby_currUser') + server = window('emby_server%s' % user_id) + token = window('emby_accessToken%s' % user_id) # Get the appropriate prefix for the websocket if "https" in server: server = server.replace('https', "wss") else: server = server.replace('http', "ws") - websocket_url = "%s?api_key=%s&deviceId=%s" % (server, token, self.deviceId) - log.info("websocket url: %s" % websocket_url) + websocket_url = "%s?api_key=%s&deviceId=%s" % (server, token, self.device_id) + log.info("websocket url: %s", websocket_url) - self.client = websocket.WebSocketApp(websocket_url, - on_message=self.on_message, - on_error=self.on_error, - on_close=self.on_close) - - self.client.on_open = self.on_open + self._client = websocket.WebSocketApp(websocket_url, + on_message=self.on_message, + on_error=self.on_error, + on_close=self.on_close) + self._client.on_open = self.on_open log.warn("----===## Starting WebSocketClient ##===----") while not self.monitor.abortRequested(): - self.client.run_forever(ping_interval=10) - if self.stopWebsocket: + self._client.run_forever(ping_interval=10) + if self._stop_websocket: break if self.monitor.waitForAbort(5): @@ -308,8 +323,8 @@ class WebSocket_Client(threading.Thread): log.warn("##===---- WebSocketClient Stopped ----===##") - def stopClient(self): + def stop_client(self): - self.stopWebsocket = True - self.client.close() - log.info("Stopping thread.") \ No newline at end of file + self._stop_websocket = True + self._client.close() + log.info("Stopping thread") diff --git a/service.py b/service.py index fa7e510e..7bcbe799 100644 --- a/service.py +++ b/service.py @@ -10,7 +10,6 @@ from datetime import datetime import xbmc import xbmcaddon -import xbmcgui ################################################################################################# @@ -55,22 +54,22 @@ class Service(object): def __init__(self): - self.clientInfo = clientinfo.ClientInfo() - self.addonName = self.clientInfo.get_addon_name() - logLevel = settings('logLevel') + self.client_info = clientinfo.ClientInfo() + self.addon_name = self.client_info.get_addon_name() + log_level = settings('logLevel') self.monitor = xbmc.Monitor() - window('emby_logLevel', value=str(logLevel)) + window('emby_logLevel', value=str(log_level)) window('emby_kodiProfile', value=xbmc.translatePath('special://profile')) # Initial logging - log.warn("======== START %s ========", self.addonName) + log.warn("======== START %s ========", self.addon_name) log.warn("Python Version: %s", sys.version) - log.warn("Platform: %s", self.clientInfo.get_platform()) + log.warn("Platform: %s", self.client_info.get_platform()) log.warn("KODI Version: %s", xbmc.getInfoLabel('System.BuildVersion')) - log.warn("%s Version: %s", self.addonName, self.clientInfo.get_version()) + log.warn("%s Version: %s", self.addon_name, self.client_info.get_version()) log.warn("Using plugin paths: %s", settings('useDirectPaths') == "0") - log.warn("Log Level: %s", logLevel) + log.warn("Log Level: %s", log_level) # Reset window props for profile switch properties = [ @@ -89,7 +88,7 @@ class Service(object): # Set the minimum database version window('emby_minDBVersion', value="1.1.63") - + def service_entry_point(self): # Important: Threads depending on abortRequest will not trigger @@ -173,7 +172,7 @@ class Service(object): message=("%s %s%s!" % (lang(33000), user.get_username().decode('utf-8'), add.decode('utf-8'))), - icon="{default}", + icon="{emby}", time=2000, sound=False) @@ -227,16 +226,16 @@ class Service(object): if settings('offlineMsg') == "true": dialog(type_="notification", heading=lang(33001), - message="%s %s" % (self.addonName, lang(33002)), - icon="{default}", + message="%s %s" % (self.addon_name, lang(33002)), + icon="{emby}", sound=False) - + self.server_online = False elif window('emby_online') == "sleep": # device going to sleep if self.websocket_running: - ws.stopClient() + ws.stop_client() ws = wsc.WebSocket_Client() self.websocket_running = False @@ -257,7 +256,7 @@ class Service(object): dialog(type_="notification", heading=lang(29999), message=lang(33003), - icon="{default}", + icon="{emby}", time=2000, sound=False) @@ -289,9 +288,9 @@ class Service(object): library.stopThread() if self.websocket_running: - ws.stopClient() + ws.stop_client() - log.warn("======== STOP %s ========", self.addonName) + log.warn("======== STOP %s ========", self.addon_name) # Delay option DELAY = int(settings('startupDelay'))