2015-03-14 08:24:59 +11:00
#################################################################################################
# utils class
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
2015-06-06 16:06:33 +10:00
import xbmcvfs
2015-03-14 08:24:59 +11:00
from ClientInformation import ClientInformation
2015-05-07 16:11:20 +10:00
import Utils as utils
2015-03-14 08:24:59 +11:00
###########################################################################
2015-05-07 16:11:20 +10:00
2015-03-14 08:24:59 +11:00
class PlayUtils ( ) :
2015-05-07 16:11:20 +10:00
_shared_state = { }
clientInfo = ClientInformation ( )
addonName = clientInfo . getAddonName ( )
addonId = clientInfo . getAddonId ( )
addon = xbmcaddon . Addon ( id = addonId )
2015-06-09 20:30:01 +10:00
audioPref = addon . getSetting ( ' Audiopref ' )
subsPref = addon . getSetting ( ' Subspref ' )
2015-05-07 16:11:20 +10:00
def __init__ ( self ) :
self . __dict__ = self . _shared_state
def logMsg ( self , msg , lvl = 1 ) :
className = self . __class__ . __name__
2015-06-16 04:42:34 +10:00
utils . logMsg ( " %s %s " % ( self . addonName , className ) , msg , int ( lvl ) )
2015-05-07 16:11:20 +10:00
2015-03-14 08:24:59 +11:00
def getPlayUrl ( self , server , id , result ) :
2015-04-14 04:56:36 +10:00
2015-05-07 16:11:20 +10: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
2015-05-14 16:05:52 +10:00
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
2015-05-07 16:11:20 +10:00
playurl = self . directStream ( result , server , id )
2015-05-14 16:05:52 +10:00
if resp == 1 :
# Remember next time
addon . setSetting ( ' playFromStream ' , " true " )
2015-05-07 16:11:20 +10:00
if not playurl :
# Try transcoding
playurl = self . transcoding ( result , server , id )
WINDOW . setProperty ( " transcoding %s " % id , " true " )
self . logMsg ( " File is transcoding. " , 1 )
2015-05-08 10:24:32 +10:00
WINDOW . setProperty ( " %s playmethod " % playurl , " Transcode " )
2015-05-07 16:11:20 +10:00
else :
self . logMsg ( " File is direct streaming. " , 1 )
2015-05-08 10:24:32 +10:00
WINDOW . setProperty ( " %s playmethod " % playurl , " DirectStream " )
2015-05-07 16:11:20 +10:00
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 )
2015-05-10 22:39:16 +10:00
WINDOW . setProperty ( " %s playmethod " % playurl . encode ( ' utf-8 ' ) , " DirectPlay " )
2015-05-07 16:11:20 +10:00
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 )
2015-05-08 10:24:32 +10:00
WINDOW . setProperty ( " %s playmethod " % playurl , " Transcode " )
2015-05-07 16:11:20 +10:00
else :
self . logMsg ( " File is direct streaming. " , 1 )
2015-05-08 10:24:32 +10:00
WINDOW . setProperty ( " %s playmethod " % playurl , " DirectStream " )
2015-05-07 16:11:20 +10:00
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 )
2015-05-08 10:24:32 +10:00
WINDOW . setProperty ( " %s playmethod " % playurl , " Transcode " )
2015-05-07 16:11:20 +10:00
except :
return False
2015-05-10 22:39:16 +10:00
return playurl . encode ( ' utf-8 ' )
2015-05-07 16:11:20 +10:00
def isDirectPlay ( self , result ) :
# Requirements for Direct play:
# FileSystem, Accessible path
2015-05-08 10:24:32 +10:00
self . addon = xbmcaddon . Addon ( id = self . addonId )
2015-03-14 08:24:59 +11:00
2015-05-08 10:24:32 +10:00
playhttp = self . addon . getSetting ( ' playFromStream ' )
2015-05-07 16:11:20 +10:00
# User forcing to play via HTTP instead of SMB
if playhttp == " true " :
2015-06-03 07:26:23 +10:00
self . logMsg ( " Can ' t direct play: Play from HTTP is enabled. " , 1 )
2015-05-07 16:11:20 +10:00
return False
canDirectPlay = result [ u ' MediaSources ' ] [ 0 ] [ u ' SupportsDirectPlay ' ]
# Make sure it's supported by server
if not canDirectPlay :
2015-06-03 07:26:23 +10:00
self . logMsg ( " Can ' t direct play: Server does not allow or support it. " , 1 )
2015-05-07 16:11:20 +10:00
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 10:38:27 +10:00
else :
2015-06-03 07:26:23 +10:00
self . logMsg ( " Can ' t direct play: Unable to locate the content. " , 1 )
2015-05-08 10:38:27 +10:00
return False
2015-05-07 16:11:20 +10:00
def directPlay ( self , result ) :
2015-05-07 16:50:34 +10:00
addon = self . addon
2015-05-07 16:11:20 +10:00
try :
# Item can be played directly
playurl = result [ u ' MediaSources ' ] [ 0 ] [ u ' Path ' ]
2015-05-10 22:39:16 +10:00
2015-05-07 16:11:20 +10:00
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
2015-05-10 22:39:16 +10:00
2015-06-08 18:11:04 +10:00
if " : " not in playurl :
self . logMsg ( " Path seems invalid: %s " % playurl )
return False
2015-05-07 16:11:20 +10:00
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
2015-06-11 15:46:47 +10:00
def directStream ( self , result , server , id , type = " Video " ) :
2015-03-14 08:24:59 +11:00
2015-05-07 16:11:20 +10:00
try :
2015-06-11 15:46:47 +10:00
if type == " Video " :
# Play with Direct Stream
playurl = " %s /mediabrowser/Videos/ %s /stream?static=true " % ( server , id )
elif type == " Audio " :
playurl = " %s /mediabrowser/Audio/ %s /stream.mp3 " % ( server , id )
return playurl
2015-05-07 16:11:20 +10:00
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-14 04:56:36 +10:00
playurl = " %s /mediabrowser/Videos/ %s /master.m3u8?mediaSourceId= %s " % ( server , id , id )
2015-05-07 16:11:20 +10:00
playurl = " %s &VideoCodec=h264&AudioCodec=aac,ac3&deviceId= %s &VideoBitrate= %s " % ( playurl , deviceId , self . getVideoBitRate ( ) * 1000 )
mediaSources = result [ u ' MediaSources ' ]
2015-06-09 20:30:01 +10:00
prefs = self . audioSubsPref ( mediaSources )
playurl = " %s %s " % ( playurl , prefs )
2015-05-07 16:11:20 +10:00
self . logMsg ( " Playurl: %s " % playurl )
2015-06-09 20:30:01 +10:00
2015-05-07 16:11:20 +10:00
return playurl
except :
self . logMsg ( " Transcoding failed. " )
return False
2015-03-14 08:24:59 +11:00
# Works out if the network quality can play directly or if transcoding is needed
def isNetworkQualitySufficient ( self , result ) :
2015-05-07 16:11:20 +10:00
2015-03-14 08:24:59 +11:00
settingsVideoBitRate = self . getVideoBitRate ( )
2015-05-07 16:11:20 +10:00
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-14 08:24:59 +11:00
def getVideoBitRate ( self ) :
2015-05-07 16:11:20 +10:00
# get the addon video quality
videoQuality = self . addon . getSetting ( ' videoBitRate ' )
2015-03-14 08:24:59 +11:00
if ( videoQuality == " 0 " ) :
2015-05-07 16:11:20 +10:00
return 664
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 1 " ) :
2015-05-07 16:11:20 +10:00
return 996
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 2 " ) :
2015-05-07 16:11:20 +10:00
return 1320
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 3 " ) :
2015-05-07 16:11:20 +10:00
return 2000
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 4 " ) :
2015-05-07 16:11:20 +10:00
return 3200
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 5 " ) :
2015-05-07 16:11:20 +10:00
return 4700
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 6 " ) :
2015-05-07 16:11:20 +10:00
return 6200
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 7 " ) :
2015-05-07 16:11:20 +10:00
return 7700
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 8 " ) :
2015-05-07 16:11:20 +10:00
return 9200
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 9 " ) :
2015-05-07 16:11:20 +10:00
return 10700
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 10 " ) :
2015-05-07 16:11:20 +10:00
return 12200
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 11 " ) :
2015-05-07 16:11:20 +10:00
return 13700
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 12 " ) :
2015-05-07 16:11:20 +10:00
return 15200
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 13 " ) :
2015-05-07 16:11:20 +10:00
return 16700
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 14 " ) :
2015-05-07 16:11:20 +10:00
return 18200
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 15 " ) :
2015-05-07 16:11:20 +10:00
return 20000
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 16 " ) :
2015-05-07 16:11:20 +10:00
return 40000
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 17 " ) :
2015-05-07 16:11:20 +10:00
return 100000
2015-03-14 08:24:59 +11:00
elif ( videoQuality == " 18 " ) :
2015-05-07 16:11:20 +10:00
return 1000000
2015-03-14 14:09:36 +11:00
else :
2015-05-07 16:11:20 +10:00
return 2147483 # max bit rate supported by server (max signed 32bit integer)
2015-03-14 14:09:36 +11:00
2015-03-14 08:24:59 +11:00
def fileExists ( self , result ) :
2015-05-07 16:11:20 +10:00
if u ' Path ' not in result :
# File has no path in server
2015-05-05 12:53:21 +10:00
return False
2015-05-07 16:11:20 +10:00
2015-06-06 16:06:33 +10:00
# Convert Emby path to a path we can verify
path = self . directPlay ( result )
2015-06-09 20:30:01 +10:00
if not path :
return False
2015-05-17 20:52:21 +10:00
try :
2015-06-06 16:06:33 +10:00
pathexists = xbmcvfs . exists ( path )
except :
pathexists = False
2015-05-07 16:11:20 +10:00
# Verify the device has access to the direct path
2015-05-17 21:27:28 +10:00
if pathexists :
2015-05-17 11:21:49 +10:00
# Local or Network path
self . logMsg ( " Path exists. " , 2 )
2015-03-14 08:24:59 +11:00
return True
2015-05-08 07:33:50 +10:00
elif " : \\ " not in path :
2015-06-06 16:06:33 +10:00
# Give benefit of the doubt.
2015-05-17 11:21:49 +10:00
self . logMsg ( " Can ' t verify path. Still try direct play. " , 2 )
2015-05-08 07:33:50 +10:00
return True
2015-03-14 08:24:59 +11:00
else :
2015-06-06 16:06:33 +10:00
self . logMsg ( " Path is detected as follow: %s . Try direct streaming. " % path , 2 )
2015-06-09 20:30:01 +10:00
return False
def audioSubsPref ( self , mediaSources ) :
addon = xbmcaddon . Addon ( id = self . addonId )
defaultAudio = mediaSources [ 0 ] [ u ' DefaultAudioStreamIndex ' ]
playurlprefs = " &AudioStreamIndex= %s " % defaultAudio
codecs = [
# Possible codecs
u ' und ' , u ' ac3 ' , u ' dts ' , u ' 5.1 ' , u ' aac ' , u ' mp3 ' , u ' dca '
]
try :
mediaStream = mediaSources [ 0 ] . get ( ' MediaStreams ' )
audiotracks = { }
substracks = { }
defaultSubs = None
for stream in mediaStream :
# Since Emby returns all possible tracks together, have to sort them.
if u ' Audio ' in stream [ u ' Type ' ] :
if u ' Language ' in stream :
audiotracks [ stream [ u ' Language ' ] ] = stream [ u ' Index ' ]
else :
audiotracks [ stream [ u ' Codec ' ] ] = stream [ u ' Index ' ]
if u ' Subtitle ' in stream [ u ' Type ' ] :
if u ' Language ' in stream :
substracks [ stream [ u ' Language ' ] ] = stream [ u ' Index ' ]
if stream [ u ' IsDefault ' ] == True :
defaultSubs = stream [ u ' Language ' ]
else :
substracks [ stream [ u ' Codec ' ] ] = stream [ u ' Index ' ]
if stream [ u ' IsDefault ' ] :
defaultSubs = stream [ u ' Codec ' ]
self . logMsg ( " %s %s %s " % ( defaultSubs , audiotracks , substracks ) , 1 )
if len ( audiotracks ) == 1 and len ( substracks ) == 0 :
# There's only one audio track and no subtitles
playurlprefs = " &AudioStreamIndex= %s " % defaultAudio
return playurlprefs
codec_intrack = False
for codec in codecs :
for track in audiotracks :
if codec in track :
codec_intrack = True
if self . audioPref in audiotracks :
self . logMsg ( " Door 1 " , 2 )
# Audio pref is available
playurlprefs = " &AudioStreamIndex= %s " % audiotracks [ self . audioPref ]
if addon . getSetting ( ' subsoverride ' ) == " true " :
# Subs are forced.
if self . subsPref in substracks :
self . logMsg ( " Door 1.1 " , 2 )
playurlprefs = " %s &SubtitleStreamIndex= %s " % ( playurlprefs , substracks [ self . subsPref ] )
else :
# Use default subs
if defaultSubs != None :
self . logMsg ( " Door 1.2 " , 2 )
playurlprefs = " %s &SubtitleStreamIndex= %s " % ( playurlprefs , substracks [ defaultSubs ] )
elif ( len ( audiotracks ) == 1 ) and not codec_intrack :
self . logMsg ( " Door 2 " , 2 )
# 1. There's one audio track.
# 2. The audio is defined as a language.
# 3. Audio pref is not available, guaranteed.
playurlprefs = " &AudioStreamIndex= %s " % defaultAudio
if self . subsPref in substracks :
self . logMsg ( " Door 2.1 " , 2 )
# Subs pref is available.
playurlprefs = " %s &SubtitleStreamIndex= %s " % ( playurlprefs , substracks [ self . subsPref ] )
else :
# Use default subs
if defaultSubs != None :
self . logMsg ( " Door 2.2 " , 2 )
playurlprefs = " %s &SubtitleStreamIndex= %s " % ( playurlprefs , substracks [ defaultSubs ] )
elif len ( audiotracks ) == 1 and codec_intrack :
self . logMsg ( " Door 3 " , 2 )
# 1. There one audio track.
# 2. The audio is undefined or a codec.
# 3. Audio track is mislabeled.
playurlprefs = " &AudioStreamIndex= %s " % defaultAudio
if self . subsPref in substracks :
# If the subtitle is available, only display
# if the setting is enabled.
if addon . getSetting ( ' subsoverride ' ) == " true " :
# Subs are forced.
self . logMsg ( " Door 3.1 " , 2 )
playurlprefs = " %s &SubtitleStreamIndex= %s " % ( playurlprefs , substracks [ self . subsPref ] )
else :
# Use default subs
if defaultSubs != None :
self . logMsg ( " Door 3.2 " , 2 )
playurlprefs = " %s &SubtitleStreamIndex= %s " % ( playurlprefs , substracks [ defaultSubs ] )
except : pass
return playurlprefs