From 79e4bd8a6a10ec8270cbc61e37672b4f4f73cdfc Mon Sep 17 00:00:00 2001
From: angelblue05 <tamara.angel05@gmail.com>
Date: Sat, 16 May 2015 01:31:08 -0500
Subject: [PATCH] Fix watched feedback and added General command

Everything in the remote control is supported except for audiostream and
subtitleindex. Turns out the watched playcount bug was indeed a
feedback, so to prevent this I'm skipping the first message that has the
itemId right after marking watched.
---
 resources/lib/DownloadUtils.py   | 14 ++++-
 resources/lib/KodiMonitor.py     |  4 +-
 resources/lib/PlaybackUtils.py   |  7 +++
 resources/lib/Player.py          | 93 +++++++++++++++++---------------
 resources/lib/WebSocketClient.py | 90 ++++++++++++++++++++++++++-----
 service.py                       |  2 +
 6 files changed, 152 insertions(+), 58 deletions(-)

diff --git a/resources/lib/DownloadUtils.py b/resources/lib/DownloadUtils.py
index dabef416..1335063a 100644
--- a/resources/lib/DownloadUtils.py
+++ b/resources/lib/DownloadUtils.py
@@ -71,8 +71,18 @@ class DownloadUtils():
         url = "{server}/mediabrowser/Sessions/Capabilities/Full"
         data = {
             'PlayableMediaTypes': "Audio,Video",
-            'SupportedCommands': "Play,Playstate,SendString,DisplayMessage,PlayNext",
-            'SupportsMediaControl': True
+            'SupportsMediaControl': True,
+            'SupportedCommands': (
+                
+                "MoveUp,MoveDown,MoveLeft,MoveRight,Select,"
+                "Back,ToggleContextMenu,ToggleFullscreen,ToggleOsdMenu,"
+                "GoHome,PageUp,NextLetter,GoToSearch,"
+                "GoToSettings,PageDown,PreviousLetter,TakeScreenshot,"
+                "VolumeUp,VolumeDown,ToggleMute,SendString,DisplayMessage,"
+
+                "Mute,Unmute,SetVolume,"
+                "Play,Playstate,PlayNext"
+            )
         }
 
         self.logMsg("Capabilities URL: %s" % url, 2)
diff --git a/resources/lib/KodiMonitor.py b/resources/lib/KodiMonitor.py
index d0884da5..653b51f6 100644
--- a/resources/lib/KodiMonitor.py
+++ b/resources/lib/KodiMonitor.py
@@ -42,8 +42,9 @@ class Kodi_Monitor(xbmc.Monitor):
                 item = jsondata.get("item").get("id")
                 type = jsondata.get("item").get("type")
                 prop = WINDOW.getProperty('Played%s%s' % (type,item))
+                processWatched = WINDOW.getProperty('played_skipWatched')
                 
-                if (playcount != None) and (prop != "true"):
+                if (playcount != None) and (prop != "true") and (processWatched != "true"):
                     WINDOW.setProperty("Played%s%s" % (type,item), "true")
                     utils.logMsg("MB# Sync","Kodi_Monitor--> VideoLibrary.OnUpdate : " + str(data),2)
                     WriteKodiVideoDB().updatePlayCountFromKodi(item, type, playcount)
@@ -87,6 +88,7 @@ class Kodi_Monitor(xbmc.Monitor):
         # triggers 3 times in a row.
         xbmc.sleep(100)
         self.WINDOW.clearProperty("Played%s%s" % (type,id))
+        self.WINDOW.clearProperty('played_skipWatched')
             
         
                 
diff --git a/resources/lib/PlaybackUtils.py b/resources/lib/PlaybackUtils.py
index 0d25fa71..3b80b597 100644
--- a/resources/lib/PlaybackUtils.py
+++ b/resources/lib/PlaybackUtils.py
@@ -108,10 +108,17 @@ class PlaybackUtils():
                 resume_result = resumeScreen.select(self.language(30105), display_list)
                 if resume_result == 0:
                     WINDOW.setProperty(playurl+"seektime", str(seekTime))
+                elif resume_result < 0:
+                    # User cancelled dialog
+                    xbmc.log("Emby player -> User cancelled resume dialog.")
+                    return
                 else:
                     WINDOW.clearProperty(playurl+"seektime")
             else:
                 WINDOW.clearProperty(playurl+"seektime")
+        else:
+            # Playback started from library
+            WINDOW.setProperty(playurl+"seektime", str(seekTime))
 
         if result.get("Type")=="Episode":
             WINDOW.setProperty(playurl+"refresh_id", result.get("SeriesId"))
diff --git a/resources/lib/Player.py b/resources/lib/Player.py
index b98eaa7c..6372e7ba 100644
--- a/resources/lib/Player.py
+++ b/resources/lib/Player.py
@@ -66,14 +66,13 @@ class Player( xbmc.Player ):
             
         addonSettings = xbmcaddon.Addon(id='plugin.video.emby')
         self.logMsg("emby Service -> played_information : " + str(self.played_information))
-        
+
         for item_url in self.played_information:
             data = self.played_information.get(item_url)
-            
             if (data is not None):
                 self.logMsg("emby Service -> item_url  : " + item_url)
                 self.logMsg("emby Service -> item_data : " + str(data))
-                
+
                 runtime = data.get("runtime")
                 currentPosition = data.get("currentPosition")
                 item_id = data.get("item_id")
@@ -81,6 +80,9 @@ class Player( xbmc.Player ):
                 currentFile = data.get("currentfile")
                 type = data.get("Type")
 
+                # Prevent websocket feedback
+                self.WINDOW.setProperty("played_itemId", item_id)
+
                 if(currentPosition != None and self.hasData(runtime)):
                     runtimeTicks = int(runtime)
                     self.logMsg("emby Service -> runtimeticks:" + str(runtimeTicks))
@@ -88,6 +90,10 @@ class Player( xbmc.Player ):
                     markPlayedAt = float(90) / 100    
 
                     self.logMsg("emby Service -> Percent Complete:" + str(percentComplete) + " Mark Played At:" + str(markPlayedAt))
+                    if percentComplete < markPlayedAt:
+                        # Do not mark as watched
+                        self.WINDOW.setProperty('played_skipWatched', 'true')
+
                     self.stopPlayback(data)
                     
                     if percentComplete > .80 and data.get("Type") == "Episode" and addonSettings.getSetting("offerDelete")=="true":
@@ -116,28 +122,16 @@ class Player( xbmc.Player ):
         self.logMsg("stopPlayback called", 2)
         
         item_id = data.get("item_id")
-        audioindex = data.get("AudioStreamIndex")
-        subtitleindex = data.get("SubtitleStreamIndex")
-        playMethod = data.get("playmethod")
         currentPosition = data.get("currentPosition")
         positionTicks = int(currentPosition * 10000000)
 
         url = "{server}/mediabrowser/Sessions/Playing/Stopped"
         
         postdata = {
-            'QueueableMediaTypes': "Video",
-            'CanSeek': True,
             'ItemId': item_id,
             'MediaSourceId': item_id,
-            'PlayMethod': playMethod,
             'PositionTicks': positionTicks
-        }
-
-        if audioindex:
-            postdata['AudioStreamIndex'] = audioindex
-
-        if subtitleindex:
-            postdata['SubtitleStreamIndex'] = subtitleindex    
+        } 
             
         self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
     
@@ -154,7 +148,7 @@ class Player( xbmc.Player ):
         data = self.played_information.get(currentFile)
 
         # only report playback if emby has initiated the playback (item_id has value)
-        if (data is not None) and (data.get("item_id") is not None):
+        if data is not None and data.get("item_id") is not None:
 
             # Get playback information
             item_id = data.get("item_id")
@@ -167,14 +161,22 @@ class Player( xbmc.Player ):
             if paused is None:
                 paused = False
 
-            #url = "{server}/mediabrowser/Sessions/Playing/Progress" 
+            # 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)
+            volume = result.get(u'result').get(u'volume')
+            muted = result.get(u'result').get(u'muted')
+
             postdata = {
                 'QueueableMediaTypes': "Video",
                 'CanSeek': True,
                 'ItemId': item_id,
                 'MediaSourceId': item_id,
+                'PlayMethod': playMethod,
                 'IsPaused': paused,
-                'PlayMethod': playMethod
+                'VolumeLevel': volume,
+                'IsMuted': muted
             }
 
             if playTime:
@@ -243,15 +245,17 @@ class Player( xbmc.Player ):
             itemType = WINDOW.getProperty(currentFile + "type")
             seekTime = WINDOW.getProperty(currentFile + "seektime")
             
-            username = WINDOW.getProperty('currUser')
-            sessionId = WINDOW.getProperty('sessionId%s' % username)
-
-            if seekTime != "":
-                PlaybackUtils().seekToPosition(int(seekTime))
+            # 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)
+            volume = result.get(u'result').get(u'volume')
+            muted = result.get(u'result').get(u'muted')
             
-            if (not item_id) or (len(item_id) == 0):
-                self.logMsg("onPlayBackStarted: No info for current playing file", 0)
-                return
+            if seekTime:
+                PlaybackUtils().seekToPosition(int(seekTime))
+            else:
+                seekTime = 0
 
             url = "{server}/mediabrowser/Sessions/Playing"
             postdata = {
@@ -259,7 +263,10 @@ class Player( xbmc.Player ):
                 'CanSeek': True,
                 'ItemId': item_id,
                 'MediaSourceId': item_id,
-                'PlayMethod': playMethod
+                'PlayMethod': playMethod,
+                'VolumeLevel': volume,
+                'PositionTicks': int(seekTime),
+                'IsMuted': muted
             }
 
             if audioindex:
@@ -268,24 +275,24 @@ class Player( xbmc.Player ):
             if subtitleindex:
                 postdata['SubtitleStreamIndex'] = subtitleindex
             
+            # Post playback to server
             self.logMsg("Sending POST play started.", 1)
-            #self.logMsg("emby Service -> Sending Post Play Started : " + url, 0)
-            self.doUtils.downloadUrl(url, postBody=postdata, type="POST")   
+            self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
             
             # save data map for updates and position calls
-            data = {}
-            data["runtime"] = runtime
-            data["item_id"] = item_id
-            data["refresh_id"] = refresh_id
-            data["currentfile"] = currentFile
-            data["AudioStreamIndex"] = audioindex
-            data["SubtitleStreamIndex"] = subtitleindex
-            data["playmethod"] = playMethod
-            data["Type"] = itemType
+            data = {
+                'runtime': runtime,
+                'item_id': item_id,
+                'refresh_id': refresh_id,
+                'currentfile': currentFile,
+                'AudioStreamIndex': audioindex,
+                'SubtitleStreamIndex': subtitleindex,
+                'playmethod': playMethod,
+                'type': itemType,
+                'PositionTicks': int(seekTime)
+            }
             self.played_information[currentFile] = data
-            
-            self.logMsg("emby Service -> ADDING_FILE : " + currentFile, 0)
-            self.logMsg("emby Service -> ADDING_FILE : " + str(self.played_information), 0)
+            self.logMsg("ADDING_FILE: %s" % self.played_information, 1)
 
             # log some playback stats
             if(itemType != None):
@@ -303,7 +310,7 @@ class Player( xbmc.Player ):
                     self.playStats[playMethod] = 1
             
             # reset in progress position
-            self.reportPlayback()
+            #self.reportPlayback()
             
     def GetPlayStats(self):
         return self.playStats
diff --git a/resources/lib/WebSocketClient.py b/resources/lib/WebSocketClient.py
index c13a027f..fd17fd93 100644
--- a/resources/lib/WebSocketClient.py
+++ b/resources/lib/WebSocketClient.py
@@ -99,8 +99,14 @@ class WebSocketThread(threading.Thread):
         
         messageType = result.get("MessageType")
         data = result.get("Data")
-        
-        if(messageType != None and messageType == "Play" and data != None):
+        WINDOW = xbmcgui.Window( 10000 )
+        playedItemId = WINDOW.getProperty('played_itemId')
+
+        if (playedItemId != '') and (playedItemId in message):
+            # Prevent feedback for watched
+            WINDOW.clearProperty('played_itemId')
+
+        elif(messageType != None and messageType == "Play" and data != None):
             itemIds = data.get("ItemIds")
             playCommand = data.get("PlayCommand")
             
@@ -144,7 +150,6 @@ class WebSocketThread(threading.Thread):
                 
         elif(messageType != None and messageType == "UserDataChanged"):
             # for now just do a full playcount sync
-            WINDOW = xbmcgui.Window( 10000 )
             self.logMsg("Message : Doing UserDataChanged", 0)
             userDataList = data.get("UserDataList")
             self.logMsg("Message : Doing UserDataChanged : UserDataList : " + str(userDataList), 0)
@@ -169,16 +174,77 @@ class WebSocketThread(threading.Thread):
 
         elif messageType == "GeneralCommand":
             
-            if data.get("Name") == "DisplayMessage":
-                message = data.get("Arguments")
-                header = message[u'Header']
-                text = message[u'Text']
-                xbmcgui.Dialog().notification(header, text)
+            command = data.get("Name")
+            arguments = data.get("Arguments")
+            
+            commandsPlayback = [
+                'Mute','Unmute','SetVolume',
+                'SetAudioStreamIndex'
+            ]
 
-            elif data.get("Name") == "SendString":
-                message = data.get("Arguments")
-                string = message[u'String']
-                xbmcgui.Dialog().notification("Emby server", string)
+            if command in commandsPlayback:
+                # These commands need to be reported back
+                if command == "Mute":
+                    xbmc.executebuiltin('Mute')
+                elif command == "Unmute":
+                    xbmc.executebuiltin('Mute')
+                elif command == "SetVolume":
+                    volume = arguments[u'Volume']
+                    xbmc.executebuiltin('SetVolume(%s[,showvolumebar])' % volume)
+                # 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[u'Header']
+                    text = arguments[u'Text']
+                    xbmcgui.Dialog().notification(header, text)
+                elif command == "SendString":
+                    string = arguments[u'String']
+                    xbmcgui.Dialog().notification("Emby server", string)
+                else:
+                    self.logMsg("Unknown command.", 1)
 
     def remove_items(self, itemsRemoved):
         
diff --git a/service.py b/service.py
index 206ece22..820e5d43 100644
--- a/service.py
+++ b/service.py
@@ -98,6 +98,7 @@ class Service():
                         ws.start()
 
                     if xbmc.Player().isPlaying():
+                        WINDOW.setProperty("Emby_Service_Timestamp", str(int(time.time())))
                         try:
                             playTime = xbmc.Player().getTime()
                             totalTime = xbmc.Player().getTotalTime()
@@ -131,6 +132,7 @@ class Service():
                             pass
 
                     else:
+                        WINDOW.setProperty("Emby_Service_Timestamp", str(int(time.time())))
                         #full sync
                         if (startupComplete == False):
                             self.logMsg("Doing_Db_Sync: syncDatabase (Started)")