mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-11-10 04:06:11 +00:00
Fix playback
Intros, additional parts, playlists should now be fully functional... I hope...
This commit is contained in:
parent
dfedf545ba
commit
ddd334f285
3 changed files with 131 additions and 137 deletions
|
@ -157,9 +157,7 @@ class Kodi_Monitor( xbmc.Monitor ):
|
||||||
|
|
||||||
elif method == "Playlist.OnClear":
|
elif method == "Playlist.OnClear":
|
||||||
self.logMsg("Clear playback properties.", 2)
|
self.logMsg("Clear playback properties.", 2)
|
||||||
utils.window('PlaylistIntroSet', clear=True)
|
utils.window('propertiesPlayback', 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
|
||||||
|
|
|
@ -55,6 +55,35 @@ 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)
|
||||||
|
|
||||||
|
|
||||||
|
############### -- 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")
|
||||||
|
listItem.setThumbnailImage(mainArt)
|
||||||
|
listItem.setIconImage(mainArt)
|
||||||
|
|
||||||
|
|
||||||
|
############### ORGANIZE CURRENT PLAYLIST ################
|
||||||
|
|
||||||
|
homeScreen = xbmc.getCondVisibility('Window.IsActive(home)')
|
||||||
|
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||||
|
startPos = max(playlist.getposition(), 0) # Can return -1
|
||||||
|
sizePlaylist = playlist.size()
|
||||||
|
|
||||||
|
propertiesPlayback = utils.window('propertiesPlayback') == "true"
|
||||||
|
introsPlaylist = False
|
||||||
|
currentPosition = startPos
|
||||||
|
|
||||||
|
self.logMsg("Playlist start position: %s" % startPos, 2)
|
||||||
|
self.logMsg("Playlist plugin position: %s" % currentPosition, 2)
|
||||||
|
self.logMsg("Playlist size: %s" % sizePlaylist, 2)
|
||||||
|
|
||||||
|
|
||||||
############### RESUME POINT ################
|
############### RESUME POINT ################
|
||||||
|
|
||||||
# Resume point for widget only
|
# Resume point for widget only
|
||||||
|
@ -66,7 +95,7 @@ class PlaybackUtils():
|
||||||
seekTime = seekTime - jumpBackSec
|
seekTime = seekTime - jumpBackSec
|
||||||
|
|
||||||
# Show the additional resume dialog if launched from a widget
|
# Show the additional resume dialog if launched from a widget
|
||||||
if xbmc.getCondVisibility('Window.IsActive(home)') and seekTime:
|
if homeScreen and seekTime:
|
||||||
# Dialog presentation
|
# Dialog presentation
|
||||||
displayTime = str(datetime.timedelta(seconds=(int(seekTime))))
|
displayTime = str(datetime.timedelta(seconds=(int(seekTime))))
|
||||||
display_list = ["%s %s" % (self.language(30106), displayTime), self.language(30107)]
|
display_list = ["%s %s" % (self.language(30106), displayTime), self.language(30107)]
|
||||||
|
@ -84,154 +113,117 @@ class PlaybackUtils():
|
||||||
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 ################
|
# We need to ensure we add the intro and additional parts only once.
|
||||||
|
# Otherwise we get a loop.
|
||||||
|
if not propertiesPlayback:
|
||||||
|
|
||||||
# In order, intros, original item requested and any additional part
|
utils.window('propertiesPlayback', value="true")
|
||||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
self.logMsg("Setting up properties in playlist.")
|
||||||
startPos = max(playlist.getposition(), 0) # Can return -1
|
|
||||||
sizePlaylist = playlist.size()
|
|
||||||
currentPosition = startPos
|
|
||||||
|
|
||||||
self.logMsg("Playlist start position: %s" % startPos, 2)
|
|
||||||
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
|
|
||||||
url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id
|
|
||||||
|
|
||||||
intros = doUtils.downloadUrl(url)
|
############### -- CHECK FOR INTROS ################
|
||||||
if intros['TotalRecordCount'] != 0:
|
|
||||||
# The server randomly returns one custom intro
|
|
||||||
intro = intros['Items'][0]
|
|
||||||
introId = intro['Id']
|
|
||||||
introListItem = xbmcgui.ListItem()
|
|
||||||
introPlayurl = PlayUtils().getPlayUrl(server, introId, intro)
|
|
||||||
|
|
||||||
self.logMsg("Intro play: %s" % introPlayurl, 1)
|
if utils.settings('disableCinema') == "false" and not seekTime:
|
||||||
|
# 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
|
||||||
|
intros = doUtils.downloadUrl(url)
|
||||||
|
|
||||||
self.setProperties(introPlayurl, intro, introListItem)
|
if intros['TotalRecordCount'] != 0:
|
||||||
self.setListItemProps(server, introId, introListItem, intro)
|
for intro in intros['Items']:
|
||||||
|
# The server randomly returns intros, process them.
|
||||||
introsPlaylist = True
|
introId = intro['Id']
|
||||||
utils.window('PlaylistIntroSet', value="true")
|
|
||||||
playlist.add(introPlayurl, introListItem, index=currentPosition)
|
introPlayurl = PlayUtils().getPlayUrl(server, introId, intro)
|
||||||
currentPosition += 1
|
introListItem = xbmcgui.ListItem()
|
||||||
|
self.logMsg("Adding Intro: %s" % introPlayurl, 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 intros
|
||||||
|
self.setProperties(introPlayurl, intro, introListItem)
|
||||||
##### Set listitem and properties for main item
|
self.setListItemProps(server, introId, introListItem, intro)
|
||||||
self.logMsg("Returned playurl: %s" % playurl, 1)
|
|
||||||
listItem.setPath(playurl)
|
playlist.add(introPlayurl, introListItem, index=currentPosition)
|
||||||
self.setProperties(playurl, result, listItem)
|
introsPlaylist = True
|
||||||
|
currentPosition += 1
|
||||||
|
|
||||||
mainArt = API().getArtwork(result, "Primary")
|
|
||||||
listItem.setThumbnailImage(mainArt)
|
|
||||||
listItem.setIconImage(mainArt)
|
|
||||||
|
|
||||||
if introsPlaylist and not sizePlaylist:
|
############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ###############
|
||||||
# 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)
|
if homeScreen and not sizePlaylist:
|
||||||
self.logMsg("Playlist current position: %s" % (currentPosition), 1)
|
# Extend our current playlist with the actual item to play only if there's no playlist first
|
||||||
playlist.add(playurl, listItem, index=currentPosition)
|
self.logMsg("Adding main item to playlist.", 1)
|
||||||
|
self.setListItemProps(server, id, listItem, result)
|
||||||
|
playlist.add(playurl, listItem, index=currentPosition)
|
||||||
|
|
||||||
|
# Ensure that additional parts are played after the main item
|
||||||
currentPosition += 1
|
currentPosition += 1
|
||||||
|
|
||||||
############### -- CHECK FOR ADDITIONAL PARTS ################
|
|
||||||
|
|
||||||
if result.get('PartCount') and not additionalProperty:
|
############### -- CHECK FOR ADDITIONAL PARTS ################
|
||||||
# Only add to the playlist after intros have played
|
|
||||||
url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
|
|
||||||
|
|
||||||
parts = doUtils.downloadUrl(url)
|
if result.get('PartCount'):
|
||||||
for part in parts['Items']:
|
# Only add to the playlist after intros have played
|
||||||
partId = part['Id']
|
partcount = result['PartCount']
|
||||||
additionalPlayurl = PlayUtils().getPlayUrl(server, partId, part)
|
url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
|
||||||
additionalListItem = xbmcgui.ListItem()
|
parts = doUtils.downloadUrl(url)
|
||||||
|
|
||||||
# Set listitem and properties for each additional parts
|
for part in parts['Items']:
|
||||||
self.logMsg("Adding to playlist: %s position: %s" % (additionalPlayurl, currentPosition), 1)
|
|
||||||
self.setProperties(additionalPlayurl, part, additionalListItem)
|
|
||||||
self.setListItemProps(server, partId, additionalListItem, part)
|
|
||||||
|
|
||||||
# Add item to playlist, after the main item
|
partId = part['Id']
|
||||||
utils.window('PlaylistAdditional', value="true")
|
additionalPlayurl = PlayUtils().getPlayUrl(server, partId, part)
|
||||||
playlist.add(additionalPlayurl, additionalListItem, index=currentPosition+1)
|
additionalListItem = xbmcgui.ListItem()
|
||||||
|
self.logMsg("Adding additional part: %s" % partcount, 1)
|
||||||
|
|
||||||
|
# Set listitem and properties for each additional parts
|
||||||
|
self.setProperties(additionalPlayurl, part, additionalListItem)
|
||||||
|
self.setListItemProps(server, partId, additionalListItem, part)
|
||||||
|
|
||||||
|
playlist.add(additionalPlayurl, additionalListItem, index=currentPosition)
|
||||||
|
currentPosition += 1
|
||||||
|
|
||||||
|
|
||||||
|
############### ADD DUMMY TO PLAYLIST #################
|
||||||
|
|
||||||
|
if (not homeScreen and introsPlaylist) or (homeScreen and sizePlaylist > 0):
|
||||||
|
# Playlist will fail on the current position. Adding dummy url
|
||||||
|
self.logMsg("Adding dummy url to counter the setResolvedUrl error.", 2)
|
||||||
|
playlist.add(playurl, index=startPos)
|
||||||
currentPosition += 1
|
currentPosition += 1
|
||||||
|
|
||||||
elif additionalProperty:
|
|
||||||
# Additional parts are already set, reset property for next time
|
# We just skipped adding properties. Reset flag for next time.
|
||||||
utils.window('PlaylistAdditional', clear=True)
|
elif propertiesPlayback:
|
||||||
self.logMsg("Clear additional property", 2)
|
self.logMsg("Resetting properties playback flag.", 2)
|
||||||
|
utils.window('propertiesPlayback', clear=True)
|
||||||
|
|
||||||
|
self.verifyPlaylist()
|
||||||
|
|
||||||
############### PLAYBACK ################
|
############### PLAYBACK ################
|
||||||
|
|
||||||
if setup == "service" or xbmc.getCondVisibility('Window.IsActive(home)'):
|
if not homeScreen and not introsPlaylist:
|
||||||
# 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)
|
|
||||||
|
|
||||||
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":
|
|
||||||
self.logMsg("Detecting playback happening via default.py.", 1)
|
|
||||||
playlistPlayer = False
|
|
||||||
|
|
||||||
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 single item.", 1)
|
||||||
self.logMsg("Processed as a playlist.", 1)
|
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
|
||||||
return xbmc.Player().play(playlist, startpos=startPos)
|
|
||||||
else: # Sent via default.py
|
elif not homeScreen:
|
||||||
self.logMsg("Processed as a single item.", 1)
|
|
||||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
|
self.logMsg("Processed as a playlist. First item is skipped.", 1)
|
||||||
|
xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.logMsg("Play as a regular item.", 1)
|
||||||
|
xbmc.Player().play(playlist, startpos=startPos)
|
||||||
|
|
||||||
|
|
||||||
|
def verifyPlaylist(self):
|
||||||
|
|
||||||
|
playlistitems = '{"jsonrpc": "2.0", "method": "Playlist.GetItems", "params": { "playlistid": 1 }, "id": 1}'
|
||||||
|
items = xbmc.executeJSONRPC(playlistitems)
|
||||||
|
self.logMsg(items, 2)
|
||||||
|
|
||||||
|
def removeFromPlaylist(self, pos):
|
||||||
|
|
||||||
|
playlistremove = '{"jsonrpc": "2.0", "method": "Playlist.Remove", "params": { "playlistid": 1, "position": %d }, "id": 1}' % pos
|
||||||
|
result = xbmc.executeJSONRPC(playlistremove)
|
||||||
|
self.logMsg(result, 1)
|
||||||
|
|
||||||
|
|
||||||
def externalSubs(self, id, playurl, mediaSources):
|
def externalSubs(self, id, playurl, mediaSources):
|
||||||
|
@ -268,6 +260,7 @@ class PlaybackUtils():
|
||||||
|
|
||||||
return externalsubs
|
return externalsubs
|
||||||
|
|
||||||
|
|
||||||
def setProperties(self, playurl, result, listItem):
|
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')
|
||||||
|
@ -290,7 +283,7 @@ class PlaybackUtils():
|
||||||
|
|
||||||
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})
|
||||||
|
@ -333,6 +326,7 @@ class PlaybackUtils():
|
||||||
|
|
||||||
listItem.setProperty('IsPlayable', 'true')
|
listItem.setProperty('IsPlayable', 'true')
|
||||||
listItem.setProperty('IsFolder', 'false')
|
listItem.setProperty('IsFolder', 'false')
|
||||||
|
listItem.setLabel(metadata['title'])
|
||||||
listItem.setInfo('video', infoLabels=metadata)
|
listItem.setInfo('video', infoLabels=metadata)
|
||||||
|
|
||||||
# Set artwork for listitem
|
# Set artwork for listitem
|
||||||
|
|
|
@ -208,7 +208,9 @@ class Player( xbmc.Player ):
|
||||||
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
||||||
|
|
||||||
# Ensure we do have a runtime
|
# Ensure we do have a runtime
|
||||||
if not runtime:
|
try:
|
||||||
|
runtime = int(runtime)
|
||||||
|
except ValueError:
|
||||||
runtime = xbmcplayer.getTotalTime()
|
runtime = xbmcplayer.getTotalTime()
|
||||||
self.logMsg("Runtime is missing, grabbing runtime from Kodi player: %s" % runtime, 1)
|
self.logMsg("Runtime is missing, grabbing runtime from Kodi player: %s" % runtime, 1)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue