mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-11-10 04:06:11 +00:00
WORK IN PROGRESS !!!
switch playback handling to playbackinfo method supported by server
This commit is contained in:
parent
8b0164d5a3
commit
e4e05a143f
2 changed files with 230 additions and 3 deletions
|
@ -509,3 +509,10 @@ class Player(xbmc.Player):
|
||||||
'PositionTicks': positionTicks
|
'PositionTicks': positionTicks
|
||||||
}
|
}
|
||||||
self.doUtils(url, postBody=postdata, action_type="POST")
|
self.doUtils(url, postBody=postdata, action_type="POST")
|
||||||
|
|
||||||
|
#If needed, close any livestreams
|
||||||
|
livestreamid = window("emby_%s.livestreamid" % self.currentFile)
|
||||||
|
if livestreamid:
|
||||||
|
url = "{server}/emby/LiveStreams/Close"
|
||||||
|
postdata = { 'LiveStreamId': livestreamid }
|
||||||
|
self.doUtils(url, postBody=postdata, action_type="POST")
|
||||||
|
|
|
@ -10,6 +10,7 @@ import xbmcgui
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
|
import downloadutils
|
||||||
from utils import window, settings, language as lang
|
from utils import window, settings, language as lang
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
@ -30,8 +31,38 @@ class PlayUtils():
|
||||||
self.userid = window('emby_currUser')
|
self.userid = window('emby_currUser')
|
||||||
self.server = window('emby_server%s' % self.userid)
|
self.server = window('emby_server%s' % self.userid)
|
||||||
|
|
||||||
|
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||||
|
|
||||||
def getPlayUrl(self):
|
def getPlayUrl(self):
|
||||||
|
'''
|
||||||
|
New style to retrieve the best playback method based on sending the profile to the server
|
||||||
|
Based on capabilities the correct path is returned, including livestreams that need to be opened by the server
|
||||||
|
TODO: Close livestream if needed (RequiresClosing in livestream source)
|
||||||
|
'''
|
||||||
|
playurl = None
|
||||||
|
pbinfo = self.getPlaybackInfo()
|
||||||
|
if pbinfo:
|
||||||
|
xbmc.log("getPlayUrl pbinfo: %s" %(pbinfo))
|
||||||
|
|
||||||
|
if pbinfo["Protocol"] == "SupportsDirectPlay":
|
||||||
|
playmethod = "DirectPlay"
|
||||||
|
elif pbinfo["Protocol"] == "SupportsDirectStream":
|
||||||
|
playmethod = "DirectStream"
|
||||||
|
elif pbinfo.get('LiveStreamId'):
|
||||||
|
playmethod = "LiveStream"
|
||||||
|
else:
|
||||||
|
playmethod = "Transcode"
|
||||||
|
|
||||||
|
playurl = pbinfo["Path"]
|
||||||
|
xbmc.log("getPlayUrl playmethod: %s - playurl: %s" %(playmethod, playurl))
|
||||||
|
window('emby_%s.playmethod' % playurl, value=playmethod)
|
||||||
|
if pbinfo["RequiresClosing"] and pbinfo.get('LiveStreamId'):
|
||||||
|
window('emby_%s.livestreamid' % playurl, value=pbinfo["LiveStreamId"])
|
||||||
|
|
||||||
|
return playurl
|
||||||
|
|
||||||
|
|
||||||
|
def getPlayUrlOld(self):
|
||||||
|
|
||||||
playurl = None
|
playurl = None
|
||||||
|
|
||||||
|
@ -40,7 +71,8 @@ class PlayUtils():
|
||||||
# Play LiveTV or recordings
|
# Play LiveTV or recordings
|
||||||
log.info("File protocol is http (livetv).")
|
log.info("File protocol is http (livetv).")
|
||||||
playurl = "%s/emby/Videos/%s/stream.ts?audioCodec=copy&videoCodec=copy" % (self.server, self.item['Id'])
|
playurl = "%s/emby/Videos/%s/stream.ts?audioCodec=copy&videoCodec=copy" % (self.server, self.item['Id'])
|
||||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
||||||
|
|
||||||
|
|
||||||
elif self.item.get('MediaSources') and self.item['MediaSources'][0]['Protocol'] == "Http":
|
elif self.item.get('MediaSources') and self.item['MediaSources'][0]['Protocol'] == "Http":
|
||||||
# Only play as http, used for channels, or online hosting of content
|
# Only play as http, used for channels, or online hosting of content
|
||||||
|
@ -409,3 +441,191 @@ class PlayUtils():
|
||||||
playurlprefs += "&AudioBitrate=192000"
|
playurlprefs += "&AudioBitrate=192000"
|
||||||
|
|
||||||
return playurlprefs
|
return playurlprefs
|
||||||
|
|
||||||
|
def getPlaybackInfo(self):
|
||||||
|
#Gets the playback Info for the current item
|
||||||
|
url = "{server}/emby/Items/%s/PlaybackInfo?format=json" %self.item['Id']
|
||||||
|
body = {
|
||||||
|
"UserId": self.userid,
|
||||||
|
"DeviceProfile": self.getDeviceProfile(),
|
||||||
|
"StartTimeTicks": 0, #TODO
|
||||||
|
"AudioStreamIndex": None, #TODO
|
||||||
|
"SubtitleStreamIndex": None, #TODO
|
||||||
|
"MediaSourceId": None,
|
||||||
|
"LiveStreamId": None
|
||||||
|
}
|
||||||
|
pbinfo = self.doUtils(url, postBody=body, action_type="POST")
|
||||||
|
xbmc.log("getPlaybackInfo: %s" %pbinfo)
|
||||||
|
mediaSource = self.getOptimalMediaSource(pbinfo["MediaSources"])
|
||||||
|
if mediaSource and mediaSource["RequiresOpening"]:
|
||||||
|
mediaSource = self.getLiveStream(pbinfo["PlaySessionId"], mediaSource)
|
||||||
|
|
||||||
|
return mediaSource
|
||||||
|
|
||||||
|
|
||||||
|
def getOptimalMediaSource(self, mediasources):
|
||||||
|
'''
|
||||||
|
Select the best possible mediasource for playback
|
||||||
|
Because we posted our deviceprofile to the server,
|
||||||
|
only streams will be returned that can actually be played by this client so no need to check bitrates etc.
|
||||||
|
'''
|
||||||
|
preferredStreamOrder = ["SupportsDirectPlay","SupportsDirectStream","SupportsTranscoding"]
|
||||||
|
bestSource = {}
|
||||||
|
for prefstream in preferredStreamOrder:
|
||||||
|
for source in mediasources:
|
||||||
|
if source[prefstream] == True:
|
||||||
|
if prefstream == "SupportsDirectPlay":
|
||||||
|
#always prefer direct play
|
||||||
|
alt_playurl = self.checkDirectPlayPath(source["Path"])
|
||||||
|
if alt_playurl:
|
||||||
|
bestSource = source
|
||||||
|
source["Path"] = alt_playurl
|
||||||
|
elif bestSource.get("BitRate",0) < source.get("Bitrate",0):
|
||||||
|
#prefer stream with highest bitrate for http sources
|
||||||
|
bestSource = source
|
||||||
|
elif not source.get("Bitrate") and source.get("RequiresOpening"):
|
||||||
|
#livestream
|
||||||
|
bestSource = source
|
||||||
|
xbmc.log("getOptimalMediaSource: %s" %bestSource)
|
||||||
|
return bestSource
|
||||||
|
|
||||||
|
def getLiveStream(self, playSessionId, mediaSource):
|
||||||
|
url = "{server}/emby/LiveStreams/Open?format=json"
|
||||||
|
body = {
|
||||||
|
"UserId": self.userid,
|
||||||
|
"DeviceProfile": self.getDeviceProfile(),
|
||||||
|
"ItemId": self.item["Id"],
|
||||||
|
"PlaySessionId": playSessionId,
|
||||||
|
"OpenToken": mediaSource["OpenToken"],
|
||||||
|
"StartTimeTicks": 0, #TODO
|
||||||
|
"AudioStreamIndex": None, #TODO
|
||||||
|
"SubtitleStreamIndex": None #TODO
|
||||||
|
}
|
||||||
|
streaminfo = self.doUtils(url, postBody=body, action_type="POST")
|
||||||
|
xbmc.log("getLiveStream: %s" %streaminfo)
|
||||||
|
return streaminfo["MediaSource"]
|
||||||
|
|
||||||
|
def checkDirectPlayPath(self, playurl):
|
||||||
|
|
||||||
|
if self.item.get('VideoType'):
|
||||||
|
# Specific format modification
|
||||||
|
if self.item['VideoType'] == "Dvd":
|
||||||
|
playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
|
||||||
|
elif self.item['VideoType'] == "BluRay":
|
||||||
|
playurl = "%s/BDMV/index.bdmv" % playurl
|
||||||
|
|
||||||
|
# Assign network protocol
|
||||||
|
if playurl.startswith('\\\\'):
|
||||||
|
playurl = playurl.replace("\\\\", "smb://")
|
||||||
|
playurl = playurl.replace("\\", "/")
|
||||||
|
|
||||||
|
if xbmcvfs.exists(playurl):
|
||||||
|
return playurl
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getDeviceProfile(self):
|
||||||
|
return {
|
||||||
|
"Name": "Kodi",
|
||||||
|
"MaxStreamingBitrate": self.getBitrate()*1000,
|
||||||
|
"MusicStreamingTranscodingBitrate": 1280000,
|
||||||
|
"TimelineOffsetSeconds": 5,
|
||||||
|
|
||||||
|
"Identification": {
|
||||||
|
"ModelName": "Kodi",
|
||||||
|
"Headers": [
|
||||||
|
{
|
||||||
|
"Name": "User-Agent",
|
||||||
|
"Value": "Kodi",
|
||||||
|
"Match": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"TranscodingProfiles": [
|
||||||
|
{
|
||||||
|
"Container": "mp3",
|
||||||
|
"AudioCodec": "mp3",
|
||||||
|
"Type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Container": "ts",
|
||||||
|
"AudioCodec": "aac",
|
||||||
|
"VideoCodec": "h264",
|
||||||
|
"Type": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Container": "jpeg",
|
||||||
|
"Type": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"DirectPlayProfiles": [
|
||||||
|
{
|
||||||
|
"Container": "",
|
||||||
|
"Type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Container": "",
|
||||||
|
"Type": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Container": "",
|
||||||
|
"Type": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"ResponseProfiles": [],
|
||||||
|
"ContainerProfiles": [],
|
||||||
|
"CodecProfiles": [],
|
||||||
|
|
||||||
|
"SubtitleProfiles": [
|
||||||
|
{
|
||||||
|
"Format": "srt",
|
||||||
|
"Method": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "sub",
|
||||||
|
"Method": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "srt",
|
||||||
|
"Method": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "ass",
|
||||||
|
"Method": 1,
|
||||||
|
"DidlMode": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "ssa",
|
||||||
|
"Method": 1,
|
||||||
|
"DidlMode": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "smi",
|
||||||
|
"Method": 1,
|
||||||
|
"DidlMode": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "dvdsub",
|
||||||
|
"Method": 1,
|
||||||
|
"DidlMode": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "pgs",
|
||||||
|
"Method": 1,
|
||||||
|
"DidlMode": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "pgssub",
|
||||||
|
"Method": 1,
|
||||||
|
"DidlMode": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Format": "sub",
|
||||||
|
"Method": 1,
|
||||||
|
"DidlMode": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue