def update_items(self, itemsToUpdate): # doing adds and updates if (len(itemsToUpdate) > 0): self.logMsg( "Message : Doing LibraryChanged : Processing Added and Updated : " + str(itemsToUpdate), 0) connection = utils.KodiSQL() cursor = connection.cursor() LibrarySync().MoviesSync(connection, cursor, fullsync=False, installFirstRun=False, itemList=itemsToUpdate) LibrarySync().TvShowsSync(connection, cursor, fullsync=False, installFirstRun=False, itemList=itemsToUpdate) cursor.close()
def user_data_update(self, userDataList): for userData in userDataList: self.logMsg( "Message : Doing UserDataChanged : UserData : " + str(userData), 0) itemId = userData.get("ItemId") if (itemId != None): self.logMsg( "Message : Doing UserDataChanged : calling updatePlayCount with ID : " + str(itemId), 0) LibrarySync().updatePlayCount(itemId)
def ServiceEntryPoint(self): kodiProfile = xbmc.translatePath("special://profile") # Server auto-detect ConnectionManager().checkServer() # Initialize important threads user = UserClient() player = Player() ws = WebSocketThread() library = LibrarySync() # Sync and progress report lastProgressUpdate = datetime.today() while not self.KodiMonitor.abortRequested(): # Before proceeding, need to make sure: # 1. Server is online # 2. User is set # 3. User has access to the server if utils.window("kodiProfile_emby") != kodiProfile: # Profile change happened, terminate this thread self.logMsg("Kodi profile was: %s and changed to: %s. Terminating old Emby thread." % (kodiProfile, utils.window("kodiProfile_emby")), 1) break if utils.window('Server_online') == "true": # Emby server is online # Verify if user is set and has access to the server if (user.currUser is not None) and user.HasAccess: # If an item is playing if xbmc.Player().isPlaying(): try: # Update and report progress playTime = xbmc.Player().getTime() totalTime = xbmc.Player().getTotalTime() currentFile = player.currentFile # Update positionticks if player.played_information.get(currentFile) is not None: player.played_information[currentFile]['currentPosition'] = playTime td = datetime.today() - lastProgressUpdate secDiff = td.seconds # Report progress to Emby server if (secDiff > 3): player.reportPlayback() lastProgressUpdate = datetime.today() elif utils.window('commandUpdate') == "true": # Received a remote control command that # requires updating immediately utils.window('commandUpdate', clear=True) player.reportPlayback() lastProgressUpdate = da4tetime.today() except Exception as e: self.logMsg("Exception in Playback Monitor Service: %s" % e, 1) pass else: # Start up events self.warn_auth = True if utils.settings('supressConnectMsg') == "false": if self.welcome_msg: # Reset authentication warnings self.welcome_msg = False # Get additional users additionalUsers = user.AdditionalUser if additionalUsers: add = ", %s" % ", ".join(additionalUsers) else: add = "" xbmcgui.Dialog().notification("Emby server", "Welcome %s%s!" % (user.currUser, add), icon="special://home/addons/plugin.video.emby/icon.png", time=2000, sound=False) # Start the Websocket Client if (self.newWebSocketThread is None): self.newWebSocketThread = "Started" ws.start() # Start the Library Sync Thread if (self.newLibraryThread is None): self.newLibraryThread = "Started" library.start() else: if (user.currUser is None) and self.warn_auth: # Alert user is not authenticated and suppress future warning self.warn_auth = False self.logMsg("Not authenticated yet.", 1) # User access is restricted. # Keep verifying until access is granted # unless server goes offline or Kodi is shut down. while user.HasAccess == False: # Verify access with an API call user.hasAccess() if utils.window('Server_online') != "true": # Server went offline break if self.KodiMonitor.waitForAbort(5): # Abort was requested while waiting. We should exit break else: # Wait until Emby server is online # or Kodi is shut down. while not self.KodiMonitor.abortRequested(): if user.getServer() == "": # No server info set in add-on settings pass elif user.getPublicUsers() == False: # Server is offline. # Alert the user and suppress future warning if self.server_online: self.logMsg("Server is offline.", 1) utils.window('Server_online', value="false") xbmcgui.Dialog().notification("Error connecting", "%s Server is unreachable." % self.addonName, icon="special://home/addons/plugin.video.emby/icon.png", sound=False) self.server_online = False else: # Server is online if not self.server_online: # Server was offline when Kodi started. # Wait for server to be fully established. if self.KodiMonitor.waitForAbort(5): # Abort was requested while waiting. break # Alert the user that server is online. xbmcgui.Dialog().notification("Emby server", "Welcome %s!" % user.currUser, icon="special://home/addons/plugin.video.emby/icon.png", time=2000, sound=False) self.server_online = True self.logMsg("Server is online and ready.", 1) utils.window('Server_online', value="true") # Start the User client if self.newUserClient is None: self.newUserClient = "Started" user.start() break if self.KodiMonitor.waitForAbort(1): # Abort was requested while waiting. break if self.KodiMonitor.waitForAbort(1): # Abort was requested while waiting. We should exit break ##### Emby thread is terminating. ##### # If music is enabled and direct stream for music is enabled # We use Kodi pathsubstitution to allow for music to play outside network # The setting needs to be set before Kodi starts. if utils.settings('enableMusicSync') == "true" and utils.settings('directstreammusic') == "true": # We need to keep track of the settings alternate = utils.settings('altip') == "true" pathsub = utils.settings('pathsub') == "true" if pathsub and not alternate: # Path sub in place, but primary address in use, remove it utils.pathsubstitution(False) elif not pathsub and alternate: # Path sub not in place, but secondary address in use, add it utils.pathsubstitution() if (self.newWebSocketThread is not None): ws.stopClient() if (self.newUserClient is not None): user.stopClient() self.logMsg("======== STOP %s ========" % self.addonName, 0)
def stopAll(self): if not self.played_information: return self.logMsg("Played_information: %s" % self.played_information, 1) # Process each items for item in self.played_information: data = self.played_information.get(item) if data: self.logMsg("Item path: %s" % item, 2) self.logMsg("Item data: %s" % data, 2) runtime = data['runtime'] currentPosition = data['currentPosition'] itemId = data['item_id'] refresh_id = data['refresh_id'] currentFile = data['currentfile'] type = data['Type'] playMethod = data['playmethod'] if currentPosition and runtime: percentComplete = (currentPosition * 10000000) / int(runtime) markPlayedAt = float(utils.settings('markPlayed')) / 100 self.logMsg( "Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt), 1) # Prevent manually mark as watched in Kodi monitor > WriteKodiVideoDB().UpdatePlaycountFromKodi() utils.window('SkipWatched%s' % itemId, "true") self.stopPlayback(data) offerDelete = utils.settings('offerDelete') == "true" offerTypeDelete = False if type == "Episode" and utils.settings( 'offerDeleteTV') == "true": offerTypeDelete = True elif type == "Movie" and utils.settings( 'offerDeleteMovies') == "true": offerTypeDelete = True if percentComplete >= markPlayedAt and offerDelete and offerTypeDelete: # Make the bigger setting be able to disable option easily. self.logMsg("Offering deletion for: %s." % itemId, 1) return_value = xbmcgui.Dialog().yesno( "Offer Delete", "Delete %s" % currentFile.split("/")[-1], "on Emby Server?") if return_value: # Delete Kodi entry before Emby listItem = [itemId] LibrarySync().removefromDB(listItem, True) # Stop transcoding if playMethod == "Transcode": self.logMsg("Transcoding for %s terminated." % itemId, 1) deviceId = self.clientInfo.getMachineId() url = "{server}/mediabrowser/Videos/ActiveEncodings?DeviceId=%s" % deviceId self.doUtils.downloadUrl(url, type="DELETE") self.played_information.clear()
class Player(xbmc.Player): # Borg - multiple instances, shared state _shared_state = {} xbmcplayer = xbmc.Player() doUtils = DownloadUtils() clientInfo = ClientInformation() ws = WebSocketThread() librarySync = LibrarySync() addonName = clientInfo.getAddonName() played_information = {} playStats = {} currentFile = None def __init__(self, *args): self.__dict__ = self._shared_state self.logMsg("Starting playback monitor.", 2) def logMsg(self, msg, lvl=1): self.className = self.__class__.__name__ utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl)) def GetPlayStats(self): return self.playStats def onPlayBackStarted(self): # Will be called when xbmc starts playing a file xbmcplayer = self.xbmcplayer self.stopAll() # Get current file try: currentFile = xbmcplayer.getPlayingFile() xbmc.sleep(300) except: currentFile = "" count = 0 while not currentFile: xbmc.sleep(100) try: currentFile = xbmcplayer.getPlayingFile() except: pass if count == 5: # try 5 times self.logMsg("Cancelling playback report...", 1) break else: count += 1 if currentFile: self.currentFile = currentFile # We may need to wait for info to be set in kodi monitor itemId = utils.window("%sitem_id" % currentFile) tryCount = 0 while not itemId: xbmc.sleep(200) itemId = utils.window("%sitem_id" % currentFile) if tryCount == 20: # try 20 times or about 10 seconds self.logMsg( "Could not find itemId, cancelling playback report...", 1) break else: tryCount += 1 else: self.logMsg( "ONPLAYBACK_STARTED: %s ITEMID: %s" % (currentFile, itemId), 0) # Only proceed if an itemId was found. runtime = utils.window("%sruntimeticks" % currentFile) refresh_id = utils.window("%srefresh_id" % currentFile) playMethod = utils.window("%splaymethod" % currentFile) itemType = utils.window("%stype" % currentFile) seekTime = xbmcplayer.getTime() # Get playback volume volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}' result = xbmc.executeJSONRPC(volume_query) result = json.loads(result) result = result.get('result') volume = result.get('volume') muted = result.get('muted') # Postdata structure to send to Emby server url = "{server}/mediabrowser/Sessions/Playing" postdata = { 'QueueableMediaTypes': "Video", 'CanSeek': True, 'ItemId': itemId, 'MediaSourceId': itemId, 'PlayMethod': playMethod, 'VolumeLevel': volume, 'PositionTicks': int(seekTime * 10000000), 'IsMuted': muted } # Get the current audio track and subtitles if playMethod == "Transcode": # property set in PlayUtils.py postdata['AudioStreamIndex'] = utils.window( "%sAudioStreamIndex" % currentFile) postdata['SubtitleStreamIndex'] = utils.window( "%sSubtitleStreamIndex" % currentFile) else: # Get the current kodi audio and subtitles and convert to Emby equivalent track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}' result = xbmc.executeJSONRPC(track_query) result = json.loads(result) result = result.get('result') try: # Audio tracks indexAudio = result['currentaudiostream']['index'] except (KeyError, TypeError): indexAudio = 0 try: # Subtitles tracks indexSubs = result['currentsubtitle']['index'] except (KeyError, TypeError): indexSubs = 0 try: # If subtitles are enabled subsEnabled = result['subtitleenabled'] except (KeyError, TypeError): subsEnabled = "" # Postdata for the audio postdata['AudioStreamIndex'] = indexAudio + 1 # Postdata for the subtitles if subsEnabled and len( xbmc.Player().getAvailableSubtitleStreams()) > 0: # Number of audiotracks to help get Emby Index audioTracks = len( xbmc.Player().getAvailableAudioStreams()) mapping = utils.window("%sIndexMapping" % currentFile) if mapping: # Set in PlaybackUtils.py self.logMsg( "Mapping for external subtitles index: %s" % mapping, 2) externalIndex = json.loads(mapping) if externalIndex.get(str(indexSubs)): # If the current subtitle is in the mapping postdata[ 'SubtitleStreamIndex'] = externalIndex[str( indexSubs)] else: # Internal subtitle currently selected postdata[ 'SubtitleStreamIndex'] = indexSubs - len( externalIndex) + audioTracks + 1 else: # Direct paths enabled scenario or no external subtitles set postdata[ 'SubtitleStreamIndex'] = indexSubs + audioTracks + 1 else: postdata['SubtitleStreamIndex'] = "" # Post playback to server self.logMsg("Sending POST play started: %s." % postdata, 2) self.doUtils.downloadUrl(url, postBody=postdata, type="POST") # Ensure we do have a runtime try: runtime = int(runtime) except ValueError: runtime = xbmcplayer.getTotalTime() self.logMsg( "Runtime is missing, grabbing runtime from Kodi player: %s" % runtime, 1) # Save data map for updates and position calls data = { 'runtime': runtime, 'item_id': itemId, 'refresh_id': refresh_id, 'currentfile': currentFile, 'AudioStreamIndex': postdata['AudioStreamIndex'], 'SubtitleStreamIndex': postdata['SubtitleStreamIndex'], 'playmethod': playMethod, 'Type': itemType, 'currentPosition': int(seekTime) } self.played_information[currentFile] = data self.logMsg("ADDING_FILE: %s" % self.played_information, 1) # log some playback stats '''if(itemType != None): if(self.playStats.get(itemType) != None): count = self.playStats.get(itemType) + 1 self.playStats[itemType] = count else: self.playStats[itemType] = 1 if(playMethod != None): if(self.playStats.get(playMethod) != None): count = self.playStats.get(playMethod) + 1 self.playStats[playMethod] = count else: self.playStats[playMethod] = 1''' def reportPlayback(self): self.logMsg("reportPlayback Called", 2) xbmcplayer = self.xbmcplayer # Get current file currentFile = self.currentFile data = self.played_information.get(currentFile) # only report playback if emby has initiated the playback (item_id has value) if data: # Get playback information itemId = data['item_id'] audioindex = data['AudioStreamIndex'] subtitleindex = data['SubtitleStreamIndex'] playTime = data['currentPosition'] playMethod = data['playmethod'] paused = data.get('paused', False) # Get playback volume volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}' result = xbmc.executeJSONRPC(volume_query) result = json.loads(result) result = result.get('result') volume = result.get('volume') muted = result.get('muted') # Postdata for the websocketclient report postdata = { 'QueueableMediaTypes': "Video", 'CanSeek': True, 'ItemId': itemId, 'MediaSourceId': itemId, 'PlayMethod': playMethod, 'PositionTicks': int(playTime * 10000000), 'IsPaused': paused, 'VolumeLevel': volume, 'IsMuted': muted } if playMethod == "Transcode": # Track can't be changed, keep reporting the same index postdata['AudioStreamIndex'] = audioindex postdata['AudioStreamIndex'] = subtitleindex else: # Get current audio and subtitles track track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}' result = xbmc.executeJSONRPC(track_query) result = json.loads(result) result = result.get('result') try: # Audio tracks indexAudio = result['currentaudiostream']['index'] except (KeyError, TypeError): indexAudio = 0 try: # Subtitles tracks indexSubs = result['currentsubtitle']['index'] except (KeyError, TypeError): indexSubs = 0 try: # If subtitles are enabled subsEnabled = result['subtitleenabled'] except (KeyError, TypeError): subsEnabled = "" # Postdata for the audio data['AudioStreamIndex'], postdata['AudioStreamIndex'] = [ indexAudio + 1 ] * 2 # Postdata for the subtitles if subsEnabled and len( xbmc.Player().getAvailableSubtitleStreams()) > 0: # Number of audiotracks to help get Emby Index audioTracks = len(xbmc.Player().getAvailableAudioStreams()) mapping = utils.window("%sIndexMapping" % currentFile) if mapping: # Set in PlaybackUtils.py self.logMsg( "Mapping for external subtitles index: %s" % mapping, 2) externalIndex = json.loads(mapping) if externalIndex.get(str(indexSubs)): # If the current subtitle is in the mapping data['SubtitleStreamIndex'], postdata[ 'SubtitleStreamIndex'] = [ externalIndex[str(indexSubs)] ] * 2 else: # Internal subtitle currently selected data['SubtitleStreamIndex'], postdata[ 'SubtitleStreamIndex'] = [ indexSubs - len(externalIndex) + audioTracks + 1 ] * 2 else: # Direct paths enabled scenario or no external subtitles set data['SubtitleStreamIndex'], postdata[ 'SubtitleStreamIndex'] = [ indexSubs + audioTracks + 1 ] * 2 else: data['SubtitleStreamIndex'], postdata[ 'SubtitleStreamIndex'] = [""] * 2 # Report progress via websocketclient postdata = json.dumps(postdata) self.logMsg("Report: %s" % postdata, 2) self.ws.sendProgressUpdate(postdata) def onPlayBackPaused(self): currentFile = self.currentFile self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2) if self.played_information.get(currentFile): self.played_information[currentFile]['paused'] = True self.reportPlayback() def onPlayBackResumed(self): currentFile = self.currentFile self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2) if self.played_information.get(currentFile): self.played_information[currentFile]['paused'] = False self.reportPlayback() def onPlayBackSeek(self, time, seekOffset): # Make position when seeking a bit more accurate currentFile = self.currentFile self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2) if self.played_information.get(currentFile): position = self.xbmcplayer.getTime() self.played_information[currentFile]['currentPosition'] = position self.reportPlayback() def onPlayBackStopped(self): # Will be called when user stops xbmc playing a file self.logMsg("ONPLAYBACK_STOPPED", 2) self.stopAll() def onPlayBackEnded(self): # Will be called when xbmc stops playing a file self.logMsg("ONPLAYBACK_ENDED", 2) self.stopAll() def stopAll(self): if not self.played_information: return self.logMsg("Played_information: %s" % self.played_information, 1) # Process each items for item in self.played_information: data = self.played_information.get(item) if data: self.logMsg("Item path: %s" % item, 2) self.logMsg("Item data: %s" % data, 2) runtime = data['runtime'] currentPosition = data['currentPosition'] itemId = data['item_id'] refresh_id = data['refresh_id'] currentFile = data['currentfile'] type = data['Type'] playMethod = data['playmethod'] if currentPosition and runtime: percentComplete = (currentPosition * 10000000) / int(runtime) markPlayedAt = float(utils.settings('markPlayed')) / 100 self.logMsg( "Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt), 1) # Prevent manually mark as watched in Kodi monitor > WriteKodiVideoDB().UpdatePlaycountFromKodi() utils.window('SkipWatched%s' % itemId, "true") self.stopPlayback(data) offerDelete = utils.settings('offerDelete') == "true" offerTypeDelete = False if type == "Episode" and utils.settings( 'offerDeleteTV') == "true": offerTypeDelete = True elif type == "Movie" and utils.settings( 'offerDeleteMovies') == "true": offerTypeDelete = True if percentComplete >= markPlayedAt and offerDelete and offerTypeDelete: # Make the bigger setting be able to disable option easily. self.logMsg("Offering deletion for: %s." % itemId, 1) return_value = xbmcgui.Dialog().yesno( "Offer Delete", "Delete %s" % currentFile.split("/")[-1], "on Emby Server?") if return_value: # Delete Kodi entry before Emby listItem = [itemId] LibrarySync().removefromDB(listItem, True) # Stop transcoding if playMethod == "Transcode": self.logMsg("Transcoding for %s terminated." % itemId, 1) deviceId = self.clientInfo.getMachineId() url = "{server}/mediabrowser/Videos/ActiveEncodings?DeviceId=%s" % deviceId self.doUtils.downloadUrl(url, type="DELETE") self.played_information.clear() def stopPlayback(self, data): self.logMsg("stopPlayback called", 2) itemId = data['item_id'] currentPosition = data['currentPosition'] positionTicks = int(currentPosition * 10000000) url = "{server}/mediabrowser/Sessions/Playing/Stopped" postdata = { 'ItemId': itemId, 'MediaSourceId': itemId, 'PositionTicks': positionTicks } self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
def __init__(self): # Parse parameters base_url = sys.argv[0] addon_handle = int(sys.argv[1]) params = urlparse.parse_qs(sys.argv[2][1:]) xbmc.log("Parameter string: %s" % sys.argv[2]) try: mode = params['mode'][0] id = params.get('id', None) if id: id = id[0] except: params = {} mode = "" ##### PLAY ITEM VIA plugin://plugin.video.emby/ ##### if "play" in mode or "playnow" in mode: entrypoint.doPlayback(id) #### DO RESET AUTH ##### elif "resetauth" in mode: entrypoint.resetAuth() ##### DO DATABASE RESET ##### elif "reset" in mode: import Utils as utils utils.reset() ##### ADD/REMOVE USER FROM SESSION ##### elif "adduser" in mode: entrypoint.addUser() ##### SYNC THEME MEDIA ##### elif "thememedia" in mode: entrypoint.getThemeMedia() ##### LAUNCH EMBY USER PREFS ##### elif "userprefs" in mode: entrypoint.userPreferences() ##### OPEN ADDON SETTINGS ##### elif "settings" in mode: xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)') ##### MANUALLY SYNC LIBRARY ##### elif "manualsync" in mode: from LibrarySync import LibrarySync LibrarySync().FullLibrarySync(True) ##### CACHE ARTWORK ##### elif "texturecache" in mode: from TextureCache import TextureCache TextureCache().FullTextureCacheSync() ##### BROWSE EMBY CHANNELS FOLDER ##### elif "channelsfolder" in mode: folderid = params['folderid'][0] entrypoint.BrowseChannels(id, folderid) ##### BROWSE EMBY CHANNELS ROOT ##### elif "channels" in mode: entrypoint.BrowseChannels(id) ##### GET NEXTUP EPISODES FOR TAGNAME ##### elif "nextup" in mode: limit = int(params['limit'][0]) entrypoint.getNextUpEpisodes(id, limit) ##### GET INPROGRESS EPISODES FOR TAGNAME ##### elif "inprogressepisodes" in mode: limit = int(params['limit'][0]) entrypoint.getInProgressEpisodes(id, limit) ##### GET RECENT EPISODES FOR TAGNAME ##### elif "recentepisodes" in mode: limit = int(params['limit'][0]) entrypoint.getRecentEpisodes(id, limit) ##### GET EXTRAFANART FOR LISTITEM ##### elif "extrafanart" in sys.argv[0]: entrypoint.getExtraFanArt() ##### SHOW ADDON NODES LISTING ##### if not mode: entrypoint.doMainListing()
import threading import json import inspect import KodiMonitor import Utils as utils from DownloadUtils import DownloadUtils from WebSocketClient import WebSocketThread from PlayUtils import PlayUtils from ClientInformation import ClientInformation from LibrarySync import LibrarySync from PlaybackUtils import PlaybackUtils from ReadEmbyDB import ReadEmbyDB from API import API librarySync = LibrarySync() # service class for playback monitoring class Player( xbmc.Player ): # Borg - multiple instances, shared state _shared_state = {} xbmcplayer = xbmc.Player() doUtils = DownloadUtils() clientInfo = ClientInformation() ws = WebSocketThread() addonName = clientInfo.getAddonName() addonId = clientInfo.getAddonId() addon = xbmcaddon.Addon(id=addonId)
from datetime import datetime cwd = xbmcaddon.Addon(id='plugin.video.emby').getAddonInfo('path') BASE_RESOURCE_PATH = xbmc.translatePath( os.path.join( cwd, 'resources', 'lib' ) ) sys.path.append(BASE_RESOURCE_PATH) import KodiMonitor import Utils as utils from LibrarySync import LibrarySync from Player import Player from DownloadUtils import DownloadUtils from ConnectionManager import ConnectionManager from ClientInformation import ClientInformation from WebSocketClient import WebSocketThread from UserClient import UserClient librarySync = LibrarySync() class Service(): newWebSocketThread = None newUserClient = None clientInfo = ClientInformation() addonName = clientInfo.getAddonName() className = None def __init__(self, *args ): self.KodiMonitor = KodiMonitor.Kodi_Monitor() addonName = self.addonName
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 xbmcplayer = xbmc.Player() currentFile = xbmcplayer.getPlayingFile() embyIndex = int(arguments['Index']) mapping = WINDOW.getProperty("%sIndexMapping" % currentFile) externalIndex = json.loads(mapping) if externalIndex: # If there's external subtitles added via PlaybackUtils for index in externalIndex: if externalIndex[index] == embyIndex: xbmcplayer.setSubtitleStream(int(index)) else: # User selected internal subtitles external = len(externalIndex) audioTracks = len( xbmcplayer.getAvailableAudioStreams()) xbmcplayer.setSubtitleStream(external + embyIndex - audioTracks - 1) else: # Emby merges audio and subtitle index together audioTracks = len( xbmcplayer.getAvailableAudioStreams()) xbmcplayer.setSubtitleStream(index - audioTracks - 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")