jellyfin-kodi/resources/lib/PlayUtils.py

334 lines
12 KiB
Python
Raw Normal View History

2015-03-13 21:24:59 +00:00
#################################################################################################
# utils class
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
from ClientInformation import ClientInformation
import Utils as utils
2015-03-13 21:24:59 +00:00
import os
###########################################################################
2015-03-13 21:24:59 +00:00
class PlayUtils():
_shared_state = {}
clientInfo = ClientInformation()
addonName = clientInfo.getAddonName()
addonId = clientInfo.getAddonId()
addon = xbmcaddon.Addon(id=addonId)
def __init__(self):
self.__dict__ = self._shared_state
def logMsg(self, msg, lvl=1):
className = self.__class__.__name__
utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl))
2015-03-13 21:24:59 +00:00
def getPlayUrl(self, server, id, result):
2015-04-13 18:56:36 +00:00
addon = self.addon
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
server = WINDOW.getProperty('server%s' % username)
if self.isDirectPlay(result):
try:
# Try direct play
playurl = self.directPlay(result)
if not playurl:
# Let user know that direct play failed
resp = xbmcgui.Dialog().select('Warning: Unable to direct play.', ['Play from HTTP', 'Play from HTTP and remember next time.'])
if resp > -1:
# Play from HTTP
playurl = self.directStream(result, server, id)
if resp == 1:
# Remember next time
addon.setSetting('playFromStream', "true")
if not playurl:
# Try transcoding
playurl = self.transcoding(result, server, id)
WINDOW.setProperty("transcoding%s" % id, "true")
self.logMsg("File is transcoding.", 1)
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
else:
self.logMsg("File is direct streaming.", 1)
WINDOW.setProperty("%splaymethod" % playurl, "DirectStream")
else:
# User decided not to proceed.
self.logMsg("Unable to direct play. Verify the following path is accessible by the device: %s. You might also need to add SMB credentials in the addon settings." % result[u'MediaSources'][0][u'Path'])
return False
else:
self.logMsg("File is direct playing.", 1)
WINDOW.setProperty("%splaymethod" % playurl.encode('utf-8'), "DirectPlay")
except:
return False
elif self.isDirectStream(result):
try:
# Try direct stream
playurl = self.directStream(result, server, id)
if not playurl:
# Try transcoding
playurl = self.transcoding(result, server, id)
WINDOW.setProperty("transcoding%s" % id, "true")
self.logMsg("File is transcoding.", 1)
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
else:
self.logMsg("File is direct streaming.", 1)
WINDOW.setProperty("%splaymethod" % playurl, "DirectStream")
except:
return False
elif self.isTranscoding(result):
try:
# Try transcoding
playurl = self.transcoding(result, server, id)
WINDOW.setProperty("transcoding%s" % id, "true")
self.logMsg("File is transcoding.", 1)
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
except:
return False
return playurl.encode('utf-8')
def isDirectPlay(self, result):
# Requirements for Direct play:
# FileSystem, Accessible path
self.addon = xbmcaddon.Addon(id=self.addonId)
2015-03-13 21:24:59 +00:00
playhttp = self.addon.getSetting('playFromStream')
# User forcing to play via HTTP instead of SMB
if playhttp == "true":
2015-06-02 21:26:23 +00:00
self.logMsg("Can't direct play: Play from HTTP is enabled.", 1)
return False
canDirectPlay = result[u'MediaSources'][0][u'SupportsDirectPlay']
# Make sure it's supported by server
if not canDirectPlay:
2015-06-02 21:26:23 +00:00
self.logMsg("Can't direct play: Server does not allow or support it.", 1)
return False
location = result[u'LocationType']
# File needs to be "FileSystem"
if u'FileSystem' in location:
# Verify if path is accessible
if self.fileExists(result):
return True
2015-05-08 00:38:27 +00:00
else:
2015-06-02 21:26:23 +00:00
self.logMsg("Can't direct play: Unable to locate the content.", 1)
2015-05-08 00:38:27 +00:00
return False
def directPlay(self, result):
2015-05-07 06:50:34 +00:00
addon = self.addon
try:
# Item can be played directly
playurl = result[u'MediaSources'][0][u'Path']
if u'VideoType' in result:
# Specific format modification
if u'Dvd' in result[u'VideoType']:
playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
elif u'BluRay' in result[u'VideoType']:
playurl = "%s/BDMV/index.bdmv" % playurl
# Network - SMB protocol
if "\\\\" in playurl:
smbuser = addon.getSetting('smbusername')
smbpass = addon.getSetting('smbpassword')
# Network share
if smbuser:
playurl = playurl.replace("\\\\", "smb://%s:%s@" % (smbuser, smbpass))
else:
playurl = playurl.replace("\\\\", "smb://")
playurl = playurl.replace("\\", "/")
if "apple.com" in playurl:
USER_AGENT = 'QuickTime/7.7.4'
playurl += "?|User-Agent=%s" % USER_AGENT
return playurl
except:
self.logMsg("Direct play failed. Trying Direct stream.", 1)
return False
def isDirectStream(self, result):
# Requirements for Direct stream:
# FileSystem or Remote, BitRate, supported encoding
canDirectStream = result[u'MediaSources'][0][u'SupportsDirectStream']
# Make sure it's supported by server
if not canDirectStream:
return False
location = result[u'LocationType']
# File can be FileSystem or Remote, not Virtual
if u'Virtual' in location:
return False
# Verify BitRate
if not self.isNetworkQualitySufficient(result):
return False
return True
def directStream(self, result, server, id):
2015-03-13 21:24:59 +00:00
try:
# Play with Direct Stream
playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
mediaSources = result[u'MediaSources']
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
playurl = "%s&AudioStreamIndex=%s" % (playurl, mediaSources[0].get('DefaultAudioStreamIndex'))
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
playurl = "%s&SubtitleStreamIndex=%s" % (playurl, mediaSources[0].get('DefaultSubtitleStreamIndex'))
self.logMsg("Playurl: %s" % playurl)
return playurl
except:
self.logMsg("Direct stream failed. Trying transcoding.", 1)
return False
def isTranscoding(self, result):
# Last resort, no requirements
# BitRate
canTranscode = result[u'MediaSources'][0][u'SupportsTranscoding']
# Make sure it's supported by server
if not canTranscode:
return False
location = result[u'LocationType']
# File can be FileSystem or Remote, not Virtual
if u'Virtual' in location:
return False
return True
def transcoding(self, result, server, id):
try:
# Play transcoding
deviceId = self.clientInfo.getMachineId()
2015-04-13 18:56:36 +00:00
playurl = "%s/mediabrowser/Videos/%s/master.m3u8?mediaSourceId=%s" % (server, id, id)
playurl = "%s&VideoCodec=h264&AudioCodec=aac,ac3&deviceId=%s&VideoBitrate=%s" % (playurl, deviceId, self.getVideoBitRate()*1000)
mediaSources = result[u'MediaSources']
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
playurl = "%s&AudioStreamIndex=%s" % (playurl, mediaSources[0][u'DefaultAudioStreamIndex'])
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
playurl = "%s&SubtitleStreamIndex=%s" % (playurl, mediaSources[0][u'DefaultSubtitleStreamIndex'])
self.logMsg("Playurl: %s" % playurl)
return playurl
except:
self.logMsg("Transcoding failed.")
return False
'''forceTranscodingCodecs = self.addon.getSetting('forceTranscodingCodecs')
# check if we should force encoding due to the forceTranscodingCodecs setting
if forceTranscodingCodecs:
forceTranscodingCodecsSet = frozenset(forceTranscodingCodecs.lower().split(','))
codecs = frozenset([mediaStream.get('Codec', None) for mediaStream in result.get('MediaStreams', [])])
commonCodecs = forceTranscodingCodecsSet & codecs
#xbmc.log("emby isDirectPlay MediaStreams codecs: %s forceTranscodingCodecs: %s, common: %s" % (codecs, forceTranscodingCodecsSet, commonCodecs))
if commonCodecs:
return False'''
2015-03-13 21:24:59 +00:00
# Works out if the network quality can play directly or if transcoding is needed
def isNetworkQualitySufficient(self, result):
2015-03-13 21:24:59 +00:00
settingsVideoBitRate = self.getVideoBitRate()
settingsVideoBitRate = settingsVideoBitRate * 1000
try:
mediaSources = result[u'MediaSources']
sourceBitRate = int(mediaSources[0][u'Bitrate'])
if settingsVideoBitRate > sourceBitRate:
return True
else:
return False
except:
return True
2015-03-13 21:24:59 +00:00
def getVideoBitRate(self):
# get the addon video quality
videoQuality = self.addon.getSetting('videoBitRate')
2015-03-13 21:24:59 +00:00
if (videoQuality == "0"):
return 664
2015-03-13 21:24:59 +00:00
elif (videoQuality == "1"):
return 996
2015-03-13 21:24:59 +00:00
elif (videoQuality == "2"):
return 1320
2015-03-13 21:24:59 +00:00
elif (videoQuality == "3"):
return 2000
2015-03-13 21:24:59 +00:00
elif (videoQuality == "4"):
return 3200
2015-03-13 21:24:59 +00:00
elif (videoQuality == "5"):
return 4700
2015-03-13 21:24:59 +00:00
elif (videoQuality == "6"):
return 6200
2015-03-13 21:24:59 +00:00
elif (videoQuality == "7"):
return 7700
2015-03-13 21:24:59 +00:00
elif (videoQuality == "8"):
return 9200
2015-03-13 21:24:59 +00:00
elif (videoQuality == "9"):
return 10700
2015-03-13 21:24:59 +00:00
elif (videoQuality == "10"):
return 12200
2015-03-13 21:24:59 +00:00
elif (videoQuality == "11"):
return 13700
2015-03-13 21:24:59 +00:00
elif (videoQuality == "12"):
return 15200
2015-03-13 21:24:59 +00:00
elif (videoQuality == "13"):
return 16700
2015-03-13 21:24:59 +00:00
elif (videoQuality == "14"):
return 18200
2015-03-13 21:24:59 +00:00
elif (videoQuality == "15"):
return 20000
2015-03-13 21:24:59 +00:00
elif (videoQuality == "16"):
return 40000
2015-03-13 21:24:59 +00:00
elif (videoQuality == "17"):
return 100000
2015-03-13 21:24:59 +00:00
elif (videoQuality == "18"):
return 1000000
else:
return 2147483 # max bit rate supported by server (max signed 32bit integer)
2015-03-13 21:24:59 +00:00
def fileExists(self, result):
if u'Path' not in result:
# File has no path in server
2015-05-05 02:53:21 +00:00
return False
path = result[u'Path']
2015-05-17 10:52:21 +00:00
try:
2015-05-17 11:27:28 +00:00
pathexists = os.path.exists(path)
2015-05-17 10:52:21 +00:00
except UnicodeEncodeError:
pathexists = os.path.exists(path.encode('utf-8'))
# Verify the device has access to the direct path
2015-05-17 11:27:28 +00:00
if pathexists:
# Local or Network path
self.logMsg("Path exists.", 2)
2015-03-13 21:24:59 +00:00
return True
elif ":\\" not in path:
# Give benefit of the doubt for nfs protocol
# Does not behave with os.path.exists
self.logMsg("Can't verify path. Still try direct play.", 2)
return True
2015-03-13 21:24:59 +00:00
else:
self.logMsg("Path is detected as a Url: %s. Try direct streaming." % path, 2)
return False