Readjust playback using playlists

Instead of fighting against Kodi, work with already set playlist and
adding items to it. Added extra comments to help understand.
This commit is contained in:
angelblue05 2015-10-11 09:25:10 -05:00
parent 5f3f45e404
commit d5f3db078f
3 changed files with 382 additions and 254 deletions

View file

@ -33,7 +33,7 @@ class Kodi_Monitor( xbmc.Monitor ):
#this library monitor is used to detect a watchedstate change by the user through the library #this library monitor is used to detect a watchedstate change by the user through the library
#as well as detect when a library item has been deleted to pass the delete to the Emby server #as well as detect when a library item has been deleted to pass the delete to the Emby server
def onNotification (self,sender,method,data): def onNotification (self, sender, method, data):
WINDOW = self.WINDOW WINDOW = self.WINDOW
downloadUtils = DownloadUtils() downloadUtils = DownloadUtils()
@ -50,6 +50,8 @@ class Kodi_Monitor( xbmc.Monitor ):
if embyid != None: if embyid != None:
playurl = xbmc.Player().getPlayingFile()
WINDOW = xbmcgui.Window( 10000 ) WINDOW = xbmcgui.Window( 10000 )
username = WINDOW.getProperty('currUser') username = WINDOW.getProperty('currUser')
userid = WINDOW.getProperty('userId%s' % username) userid = WINDOW.getProperty('userId%s' % username)
@ -153,6 +155,12 @@ class Kodi_Monitor( xbmc.Monitor ):
xbmc.log('Deleting via URL: ' + url) xbmc.log('Deleting via URL: ' + url)
DownloadUtils().downloadUrl(url, type="DELETE") DownloadUtils().downloadUrl(url, type="DELETE")
elif method == "Playlist.OnClear":
self.logMsg("Clear playback properties.", 2)
utils.window('PlaylistIntroSet', clear=True)
utils.window('PlaylistsetDummy', clear=True)
utils.window('PlaylistAdditional', clear=True)
def clearProperty(self, type, id): def clearProperty(self, type, id):
# The sleep is necessary since VideoLibrary.OnUpdate # The sleep is necessary since VideoLibrary.OnUpdate
# triggers 4 times in a row. # triggers 4 times in a row.

View file

@ -43,12 +43,11 @@ class PlaybackUtils():
username = utils.window('currUser') username = utils.window('currUser')
server = utils.window('server%s' % username) server = utils.window('server%s' % username)
listItem = xbmcgui.ListItem()
id = result['Id'] id = result['Id']
userdata = result['UserData'] userdata = result['UserData']
# Get the playurl - direct play, direct stream or transcoding # Get the playurl - direct play, direct stream or transcoding
playurl = PlayUtils().getPlayUrl(server, id, result) playurl = PlayUtils().getPlayUrl(server, id, result)
listItem = xbmcgui.ListItem()
if utils.window('playurlFalse') == "true": if utils.window('playurlFalse') == "true":
# Playurl failed - set in PlayUtils.py # Playurl failed - set in PlayUtils.py
@ -56,6 +55,8 @@ class PlaybackUtils():
self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1) self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1)
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem) return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
############### RESUME POINT ################
# Resume point for widget only # Resume point for widget only
timeInfo = api.getTimeInfo(result) timeInfo = api.getTimeInfo(result)
jumpBackSec = int(utils.settings('resumeJumpBack')) jumpBackSec = int(utils.settings('resumeJumpBack'))
@ -79,73 +80,164 @@ class PlaybackUtils():
# User selected to start from beginning # User selected to start from beginning
seekTime = 0 seekTime = 0
elif resume_result < 0: else: # User cancelled the dialog
# User cancelled the dialog
self.logMsg("User cancelled resume dialog.", 1) self.logMsg("User cancelled resume dialog.", 1)
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem) return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
############### ORGANIZE CURRENT PLAYLIST ################
# In order, intros, original item requested and any additional parts # In order, intros, original item requested and any additional part
playstack = [] playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
startPos = max(playlist.getposition(), 0) # Can return -1
sizePlaylist = playlist.size()
currentPosition = startPos
# Check for intros self.logMsg("Playlist start position: %s" % startPos, 2)
if utils.settings('disableCinema') == "false" and not seekTime: self.logMsg("Playlist current position: %s" % currentPosition, 2)
self.logMsg("Playlist size: %s" % sizePlaylist, 2)
# Properties to ensure we have have proper playlists with additional items.
introsPlaylist = False
introProperty = utils.window('PlaylistIntroSet') == "true"
dummyProperty = utils.window('PlaylistsetDummy') == "true"
additionalProperty = utils.window('PlaylistAdditional') == "true"
############### -- CHECK FOR INTROS ################
if utils.settings('disableCinema') == "false" and not introProperty and not seekTime:
# if we have any play them when the movie/show is not being resumed # if we have any play them when the movie/show is not being resumed
url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
intros = doUtils.downloadUrl(url) intros = doUtils.downloadUrl(url)
if intros['TotalRecordCount'] != 0: if intros['TotalRecordCount'] != 0:
for intro in intros['Items']: # The server randomly returns one custom intro
introPlayurl = PlayUtils().getPlayUrl(server, intro['Id'], intro) intro = intros['Items'][0]
self.setProperties(introPlayurl, intro) introId = intro['Id']
playstack.append(introPlayurl) introListItem = xbmcgui.ListItem()
introPlayurl = PlayUtils().getPlayUrl(server, introId, intro)
# Add original item self.logMsg("Intro play: %s" % introPlayurl, 1)
playstack.append(playurl)
self.setProperties(playurl, result) self.setProperties(introPlayurl, intro, introListItem)
self.setListItemProps(server, introId, introListItem, intro)
introsPlaylist = True
utils.window('PlaylistIntroSet', value="true")
playlist.add(introPlayurl, introListItem, index=currentPosition)
currentPosition += 1
elif introProperty:
# Play main item, do not play the intro since we already played it. Reset property for next time.
utils.window('PlaylistIntroSet', clear=True)
self.logMsg("Clear intro property.", 2)
############### -- SETUP MAIN ITEM ################
##### Set listitem and properties for main item
self.logMsg("Returned playurl: %s" % playurl, 1)
listItem.setPath(playurl)
self.setProperties(playurl, result, listItem)
mainArt = API().getArtwork(result, "Primary") mainArt = API().getArtwork(result, "Primary")
listItem.setThumbnailImage(mainArt) listItem.setThumbnailImage(mainArt)
listItem.setIconImage(mainArt) listItem.setIconImage(mainArt)
# Get additional parts/playurl if introsPlaylist and not sizePlaylist:
if result.get('PartCount'): # Extend our current playlist with the actual item to play only if there's no playlist first
self.logMsg("No playlist detected at the start. Creating playlist with intro and play item.", 1)
self.logMsg("Playlist current position: %s" % (currentPosition), 1)
playlist.add(playurl, listItem, index=currentPosition)
currentPosition += 1
############### -- CHECK FOR ADDITIONAL PARTS ################
if result.get('PartCount') and not additionalProperty:
# Only add to the playlist after intros have played
url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
parts = doUtils.downloadUrl(url) parts = doUtils.downloadUrl(url)
for part in parts['Items']: for part in parts['Items']:
additionalPlayurl = PlayUtils().getPlayUrl(server, part['Id'], part) partId = part['Id']
self.setProperties(additionalPlayurl, part) additionalPlayurl = PlayUtils().getPlayUrl(server, partId, part)
playstack.append(additionalPlayurl) additionalListItem = xbmcgui.ListItem()
# Set listitem and properties for each additional parts
self.logMsg("Adding to playlist: %s position: %s" % (additionalPlayurl, currentPosition), 1)
self.setProperties(additionalPlayurl, part, additionalListItem)
self.setListItemProps(server, partId, additionalListItem, part)
if len(playstack) > 1: # Add item to playlist, after the main item
# Convert list in stack:// playurl utils.window('PlaylistAdditional', value="true")
playMethod = utils.window('%splaymethod' % playurl) playlist.add(additionalPlayurl, additionalListItem, index=currentPosition+1)
playurl = "stack://%s" % " , ".join(playstack) currentPosition += 1
# Set new window properties for combined playurl
utils.window("%splaymethod" % playurl, value=playMethod)
self.setProperties(playurl, result)
self.logMsg("Returned playurl: %s" % playurl, 1) elif additionalProperty:
listItem.setPath(playurl) # Additional parts are already set, reset property for next time
utils.window('PlaylistAdditional', clear=True)
self.logMsg("Clear additional property", 2)
if utils.window("%splaymethod" % playurl) != "Transcode": ############### PLAYBACK ################
# Only for direct play and direct stream
# Append external subtitles to stream
subtitleList = self.externalSubs(id, playurl, server, result['MediaSources'])
listItem.setSubtitles(subtitleList)
# Launch the playback - only set the listitem props if we're not using the setresolvedurl approach
if setup == "service" or xbmc.getCondVisibility('Window.IsActive(home)'): if setup == "service" or xbmc.getCondVisibility('Window.IsActive(home)'):
# Sent via websocketclient.py or default.py but via widgets # Sent via websocketclient.py or default.py but via widgets
self.logMsg("Detecting playback happening via service.py or home menu.", 1)
self.setListItemProps(server, id, listItem, result) self.setListItemProps(server, id, listItem, result)
xbmc.Player().play(playurl, listItem)
playlistPlayer = False
if introsPlaylist and not sizePlaylist:
# Extend our current playlist with the actual item to play only if there's no playlist first
playlistPlayer = True
elif sizePlaylist > 0 and not dummyProperty:
# Playlist will fail on the current position. Adding dummy url
playlist.add(playurl, index=startPos)
self.logMsg("Adding dummy path as replacement for position: %s" % startPos, 2)
utils.window('PlaylistsetDummy', value="true")
playlistPlayer = True
elif dummyProperty:
# Already failed, play the item as a single item
utils.window('PlaylistsetDummy', clear=True)
self.logMsg("Clear dummy property.", 2)
if playlistPlayer:
self.logMsg("Processed as a playlist.", 1)
return xbmc.Player().play(playlist)
else:
self.logMsg("Processed as a single item.", 1)
return xbmc.Player().play(playurl, listItem)
elif setup == "default": elif setup == "default":
# Sent via default.py self.logMsg("Detecting playback happening via default.py.", 1)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem) playlistPlayer = False
def externalSubs(self, id, playurl, server, mediaSources): if sizePlaylist > 0 and not dummyProperty:
# Playlist will fail on the current position. Adding dummy url
playlist.add(playurl, index=startPos)
self.logMsg("Adding dummy path as replacement for position: %s" % startPos, 2)
utils.window('PlaylistsetDummy', value="true")
playlistPlayer = True
elif dummyProperty:
# Already failed, play the item as a single item
utils.window('PlaylistsetDummy', clear=True)
self.logMsg("Clear dummy property.", 2)
if playlistPlayer:
self.logMsg("Processed as a playlist.", 1)
return xbmc.Player().play(playlist, startpos=startPos)
else: # Sent via default.py
self.logMsg("Processed as a single item.", 1)
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
def externalSubs(self, id, playurl, mediaSources):
username = utils.window('currUser')
server = utils.window('server%s' % username)
externalsubs = [] externalsubs = []
mapping = {} mapping = {}
@ -176,7 +268,7 @@ class PlaybackUtils():
return externalsubs return externalsubs
def setProperties(self, playurl, result): def setProperties(self, playurl, result, listItem):
# Set runtimeticks, type, refresh_id and item_id # Set runtimeticks, type, refresh_id and item_id
id = result.get('Id') id = result.get('Id')
type = result.get('Type', "") type = result.get('Type', "")
@ -190,9 +282,15 @@ class PlaybackUtils():
else: else:
utils.window("%srefresh_id" % playurl, value=id) utils.window("%srefresh_id" % playurl, value=id)
if utils.window("%splaymethod" % playurl) != "Transcode":
# Only for direct play and direct stream
# Append external subtitles to stream
subtitleList = self.externalSubs(id, playurl, result['MediaSources'])
listItem.setSubtitles(subtitleList)
def setArt(self, list, name, path): def setArt(self, list, name, path):
if name in ("thumb", "fanart_image", "small_poster", "tiny_poster", "medium_landscape", "medium_poster", "small_fanartimage", "medium_fanartimage", "fanart_noindicators"): if name in {"thumb", "fanart_image", "small_poster", "tiny_poster", "medium_landscape", "medium_poster", "small_fanartimage", "medium_fanartimage", "fanart_noindicators"}:
list.setProperty(name, path) list.setProperty(name, path)
else: else:
list.setArt({name:path}) list.setArt({name:path})
@ -298,7 +396,7 @@ class PlaybackUtils():
def AddToPlaylist(self, itemIds): def AddToPlaylist(self, itemIds):
self.logMsg("PlayBackUtils", "== ENTER: PLAYAllItems ==") self.logMsg("== ENTER: PLAYAllItems ==")
doUtils = self.doUtils doUtils = self.doUtils
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)

View file

@ -70,145 +70,179 @@ class Player( xbmc.Player ):
xbmcplayer = self.xbmcplayer xbmcplayer = self.xbmcplayer
self.stopAll() self.stopAll()
# Get current file - if stack://, get currently playing item # Get current file
currentFile = xbmcplayer.getPlayingFile() try:
if "stack://" in currentFile: currentFile = xbmcplayer.getPlayingFile()
self.stackFiles = currentFile xbmc.sleep(200)
currentFile = self.currentStackItem(currentFile) except:
else: currentFile = ""
self.stackFiles = None count = 0
self.currentFile = currentFile while not currentFile:
self.stackElapsed = 0
self.logMsg("ONPLAYBACK_STARTED: %s" % currentFile, 0) xbmc.sleep(100)
try:
currentFile = xbmcplayer.getPlayingFile()
except: pass
# We may need to wait for info to be set in kodi monitor if count == 5: # try 5 times
itemId = utils.window("%sitem_id" % currentFile) self.logMsg("Cancelling playback report...", 1)
tryCount = 0 break
while not itemId: else: count += 1
xbmc.sleep(500)
if currentFile:
# if stack://, get currently playing item
if "stack://" in currentFile:
self.stackFiles = currentFile
currentFile = self.currentStackItem(currentFile)
else:
self.stackFiles = None
self.currentFile = currentFile
self.stackElapsed = 0
self.logMsg("ONPLAYBACK_STARTED: %s" % currentFile, 0)
# We may need to wait for info to be set in kodi monitor
itemId = utils.window("%sitem_id" % currentFile) itemId = utils.window("%sitem_id" % currentFile)
if tryCount == 20: # try 20 times or about 10 seconds tryCount = 0
break while not itemId:
else: tryCount += 1
else: xbmc.sleep(200)
# Only proceed if an itemId was found. itemId = utils.window("%sitem_id" % currentFile)
runtime = utils.window("%sruntimeticks" % currentFile) if tryCount == 20: # try 20 times or about 10 seconds
refresh_id = utils.window("%srefresh_id" % currentFile) self.logMsg("Could not find itemId, cancelling playback report...", 1)
playMethod = utils.window("%splaymethod" % currentFile) break
itemType = utils.window("%stype" % currentFile) else: tryCount += 1
mapping = utils.window("%sIndexMapping" % currentFile)
seekTime = xbmc.Player().getTime()
self.logMsg("Mapping for subtitles index: %s" % mapping, 2)
# 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('result').get('volume')
muted = result.get('result').get('muted')
url = "{server}/mediabrowser/Sessions/Playing"
postdata = {
'QueueableMediaTypes': "Video",
'CanSeek': True,
'ItemId': itemId,
'MediaSourceId': itemId,
'PlayMethod': playMethod,
'VolumeLevel': volume,
'PositionTicks': int(seekTime * 10000000) - int(self.stackElapsed * 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: else:
track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}' # Only proceed if an itemId was found.
result = xbmc.executeJSONRPC(track_query) 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 = json.loads(result)
result = result.get('result')
# Audio tracks volume = result.get('volume')
indexAudio = result.get('result', 0) muted = result.get('muted')
if indexAudio:
indexAudio = indexAudio.get('currentaudiostream', {}).get('index', 0)
# Subtitles tracks
indexSubs = result.get('result', 0)
if indexSubs:
indexSubs = indexSubs.get('currentsubtitle', {}).get('index', 0)
# If subtitles are enabled
subsEnabled = result.get('result', "")
if subsEnabled:
subsEnabled = subsEnabled.get('subtitleenabled', "")
# Postdata for the audio and subs tracks # Postdata structure to send to Emby server
audioTracks = len(xbmc.Player().getAvailableAudioStreams()) url = "{server}/mediabrowser/Sessions/Playing"
postdata['AudioStreamIndex'] = indexAudio + 1 postdata = {
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0: 'QueueableMediaTypes': "Video",
'CanSeek': True,
'ItemId': itemId,
'MediaSourceId': itemId,
'PlayMethod': playMethod,
'VolumeLevel': volume,
'PositionTicks': int(seekTime * 10000000) - int(self.stackElapsed * 10000000),
'IsMuted': muted
}
if mapping: # Get the current audio track and subtitles
externalIndex = json.loads(mapping) if playMethod == "Transcode":
else: # Direct paths scenario # property set in PlayUtils.py
externalIndex = "" postdata['AudioStreamIndex'] = utils.window("%sAudioStreamIndex" % currentFile)
postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex" % currentFile)
if externalIndex: else:
# If there's external subtitles added via PlaybackUtils # Get the current kodi audio and subtitles and convert to Emby equivalent
if externalIndex.get(str(indexSubs)): track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
# If the current subtitle is in the mapping result = xbmc.executeJSONRPC(track_query)
postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)] result = json.loads(result)
else: result = result.get('result')
# Internal subtitle currently selected
external = len(externalIndex) try: # Audio tracks
postdata['SubtitleStreamIndex'] = indexSubs - external + audioTracks + 1 indexAudio = result['currentaudiostream']['index']
except KeyError:
indexAudio = 0
try: # Subtitles tracks
indexSubs = result['currentsubtitle']['index']
except KeyError:
indexSubs = 0
try: # If subtitles are enabled
subsEnabled = result['subtitleenabled']
except KeyError:
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: else:
# No external subtitles added via PlayUtils postdata['SubtitleStreamIndex'] = ""
postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
else:
postdata['SubtitleStreamIndex'] = ""
# Post playback to server
self.logMsg("Sending POST play started.", 1)
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
# save data map for updates and position calls # Post playback to server
data = { self.logMsg("Sending POST play started: %s." % postdata, 2)
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
'runtime': runtime, # Ensure we do have a runtime
'item_id': itemId, if not runtime:
'refresh_id': refresh_id, runtime = xbmcplayer.getTotalTime()
'currentfile': currentFile, self.logMsg("Runtime is missing, grabbing runtime from Kodi player: %s" % runtime, 1)
'AudioStreamIndex': postdata['AudioStreamIndex'],
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
'playmethod': playMethod,
'Type': itemType,
'currentPosition': int(seekTime) - int(self.stackElapsed)
}
self.played_information[currentFile] = data # Save data map for updates and position calls
self.logMsg("ADDING_FILE: %s" % self.played_information, 1) data = {
# log some playback stats 'runtime': runtime,
'''if(itemType != None): 'item_id': itemId,
if(self.playStats.get(itemType) != None): 'refresh_id': refresh_id,
count = self.playStats.get(itemType) + 1 'currentfile': currentFile,
self.playStats[itemType] = count 'AudioStreamIndex': postdata['AudioStreamIndex'],
else: 'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
self.playStats[itemType] = 1 'playmethod': playMethod,
'Type': itemType,
'currentPosition': int(seekTime) - int(self.stackElapsed)
}
if(playMethod != None): self.played_information[currentFile] = data
if(self.playStats.get(playMethod) != None): self.logMsg("ADDING_FILE: %s" % self.played_information, 1)
count = self.playStats.get(playMethod) + 1
self.playStats[playMethod] = count # log some playback stats
else: '''if(itemType != None):
self.playStats[playMethod] = 1''' 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): def reportPlayback(self):
@ -221,25 +255,26 @@ class Player( xbmc.Player ):
# only report playback if emby has initiated the playback (item_id has value) # only report playback if emby has initiated the playback (item_id has value)
if data: if data:
# Get playback information # Get playback information
itemId = data.get("item_id") itemId = data['item_id']
audioindex = data.get("AudioStreamIndex") audioindex = data['AudioStreamIndex']
subtitleindex = data.get("SubtitleStreamIndex") subtitleindex = data['SubtitleStreamIndex']
playTime = data.get("currentPosition") playTime = data['currentPosition']
playMethod = data.get("playmethod") playMethod = data['playmethod']
paused = data.get("paused") paused = data.get('paused', False)
if paused is None:
paused = False
# Get playback volume # Get playback volume
volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}' volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
result = xbmc.executeJSONRPC(volume_query) result = xbmc.executeJSONRPC(volume_query)
result = json.loads(result) result = json.loads(result)
volume = result.get('result').get('volume') result = result.get('result')
muted = result.get('result').get('muted')
volume = result.get('volume')
muted = result.get('muted')
# Postdata for the websocketclient report
postdata = { postdata = {
'QueueableMediaTypes': "Video", 'QueueableMediaTypes': "Video",
@ -247,78 +282,67 @@ class Player( xbmc.Player ):
'ItemId': itemId, 'ItemId': itemId,
'MediaSourceId': itemId, 'MediaSourceId': itemId,
'PlayMethod': playMethod, 'PlayMethod': playMethod,
'PositionTicks': int(playTime * 10000000) - int(self.stackElapsed * 10000000),
'IsPaused': paused, 'IsPaused': paused,
'VolumeLevel': volume, 'VolumeLevel': volume,
'IsMuted': muted 'IsMuted': muted
} }
if playTime:
postdata['PositionTicks'] = int(playTime * 10000000) - int(self.stackElapsed * 10000000)
if playMethod == "Transcode": if playMethod == "Transcode":
# Track can't be changed, keep reporting the same index
data['AudioStreamIndex'] = audioindex postdata['AudioStreamIndex'] = audioindex
data['SubtitleStreamIndex'] = subtitleindex postdata['AudioStreamIndex'] = subtitleindex
else: else:
# Get current audio and subtitles track # Get current audio and subtitles track
track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}' track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties", "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
result = xbmc.executeJSONRPC(track_query) result = xbmc.executeJSONRPC(track_query)
result = json.loads(result) result = json.loads(result)
# Audio tracks result = result.get('result')
indexAudio = result.get('result', 0)
if indexAudio:
indexAudio = indexAudio.get('currentaudiostream', {}).get('index', 0)
# Subtitles tracks
indexSubs = result.get('result', 0)
if indexSubs:
indexSubs = indexSubs.get('currentsubtitle', {}).get('index', 0)
# If subtitles are enabled
subsEnabled = result.get('result', "")
if subsEnabled:
subsEnabled = subsEnabled.get('subtitleenabled', "")
# Convert back into an Emby index try: # Audio tracks
audioTracks = len(xbmc.Player().getAvailableAudioStreams()) indexAudio = result['currentaudiostream']['index']
indexAudio = indexAudio + 1 except KeyError:
indexAudio = 0
try: # Subtitles tracks
indexSubs = result['currentsubtitle']['index']
except KeyError:
indexSubs = 0
try: # If subtitles are enabled
subsEnabled = result['subtitleenabled']
except KeyError:
subsEnabled = ""
# Postdata for the audio
data['AudioStreamIndex'], postdata['AudioStreamIndex'] = [indexAudio + 1] * 2
# Postdata for the subtitles
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0: if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
WINDOW = xbmcgui.Window(10000)
mapping = WINDOW.getProperty("%sIndexMapping" % currentFile)
if mapping: # 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) externalIndex = json.loads(mapping)
else: # Direct paths scenario
externalIndex = ""
if externalIndex:
# If there's external subtitles added via PlaybackUtils
if externalIndex.get(str(indexSubs)): if externalIndex.get(str(indexSubs)):
# If the current subtitle is in the mapping # If the current subtitle is in the mapping
indexSubs = externalIndex[str(indexSubs)] data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [externalIndex[str(indexSubs)]] * 2
else: else:
# Internal subtitle currently selected # Internal subtitle currently selected
external = len(externalIndex) data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs - len(externalIndex) + audioTracks + 1] * 2
indexSubs = indexSubs - external + audioTracks + 1
else:
# No external subtitles added via PlayUtils
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
indexSubs = indexSubs + audioTracks + 1
else:
indexSubs = ""
if audioindex == indexAudio: else: # Direct paths enabled scenario or no external subtitles set
postdata['AudioStreamIndex'] = audioindex data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs + audioTracks + 1] * 2
else: else:
postdata['AudioStreamIndex'] = indexAudio data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [""] * 2
data['AudioStreamIndex'] = indexAudio
if subtitleindex == indexSubs:
postdata['SubtitleStreamIndex'] = subtitleindex
else:
postdata['SubtitleStreamIndex'] = indexSubs
data['SubtitleStreamIndex'] = indexSubs
# Report progress via websocketclient
postdata = json.dumps(postdata) postdata = json.dumps(postdata)
self.logMsg("Report: %s" % postdata, 2) self.logMsg("Report: %s" % postdata, 2)
self.ws.sendProgressUpdate(postdata) self.ws.sendProgressUpdate(postdata)
@ -329,9 +353,9 @@ class Player( xbmc.Player ):
self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2) self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
if self.played_information.get(currentFile): if self.played_information.get(currentFile):
self.played_information[currentFile]['paused'] = "true" self.played_information[currentFile]['paused'] = True
self.reportPlayback() self.reportPlayback()
def onPlayBackResumed( self ): def onPlayBackResumed( self ):
@ -339,22 +363,20 @@ class Player( xbmc.Player ):
self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2) self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
if self.played_information.get(currentFile): if self.played_information.get(currentFile):
self.played_information[currentFile]['paused'] = "false" self.played_information[currentFile]['paused'] = False
self.reportPlayback() self.reportPlayback()
def onPlayBackSeek( self, time, seekOffset ): def onPlayBackSeek( self, time, seekOffset ):
self.logMsg("PLAYBACK_SEEK", 2)
xbmcplayer = self.xbmcplayer
# Make position when seeking a bit more accurate # Make position when seeking a bit more accurate
position = xbmcplayer.getTime()
currentFile = self.currentFile currentFile = self.currentFile
self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
if self.played_information.get(currentFile): if self.played_information.get(currentFile):
position = self.xbmcplayer.getTime()
self.played_information[currentFile]['currentPosition'] = position self.played_information[currentFile]['currentPosition'] = position
self.reportPlayback() self.reportPlayback()
def onPlayBackStopped( self ): def onPlayBackStopped( self ):
# Will be called when user stops xbmc playing a file # Will be called when user stops xbmc playing a file
@ -371,7 +393,7 @@ class Player( xbmc.Player ):
if not self.played_information: if not self.played_information:
return return
self.logMsg("Played_information: %s" % str(self.played_information), 1) self.logMsg("Played_information: %s" % self.played_information, 1)
# Process each items # Process each items
for item in self.played_information: for item in self.played_information:
@ -379,15 +401,15 @@ class Player( xbmc.Player ):
if data: if data:
self.logMsg("Item path: %s" % item, 2) self.logMsg("Item path: %s" % item, 2)
self.logMsg("Item data: %s" % str(data), 2) self.logMsg("Item data: %s" % data, 2)
runtime = data.get('runtime') runtime = data['runtime']
currentPosition = data.get('currentPosition') currentPosition = data['currentPosition']
itemId = data.get('item_id') itemId = data['item_id']
refresh_id = data.get('refresh_id') refresh_id = data['refresh_id']
currentFile = data.get('currentfile') currentFile = data['currentfile']
type = data.get('Type') type = data['Type']
playMethod = data.get('playmethod') playMethod = data['playmethod']
if currentPosition and runtime: if currentPosition and runtime:
percentComplete = (currentPosition * 10000000) / int(runtime) percentComplete = (currentPosition * 10000000) / int(runtime)
@ -409,7 +431,7 @@ class Player( xbmc.Player ):
if percentComplete >= markPlayedAt and offerDelete: if percentComplete >= markPlayedAt and offerDelete:
# Item could be stacked, so only offer to delete the main item. # Item could be stacked, so only offer to delete the main item.
if not self.stackFiles or itemId == utils.window('%sitem_id' % self.stackFiles): if not self.stackFiles or itemId == utils.window('%sitem_id' % self.stackFiles):
return_value = xbmcgui.Dialog().yesno("Offer Delete", "Delete %s" % data.get('currentfile').split("/")[-1], "on Emby Server?") return_value = xbmcgui.Dialog().yesno("Offer Delete", "Delete %s" % currentFile.split("/")[-1], "on Emby Server?")
if return_value: if return_value:
# Delete Kodi entry before Emby # Delete Kodi entry before Emby
listItem = [itemId] listItem = [itemId]