2015-07-18 08:08:05 +00:00
# -*- coding: utf-8 -*-
2015-03-13 21:24:59 +00:00
#################################################################################################
import xbmc
import xbmcgui
2015-06-06 06:06:33 +00:00
import xbmcvfs
2015-03-13 21:24:59 +00:00
from ClientInformation import ClientInformation
2015-05-07 06:11:20 +00:00
import Utils as utils
2015-03-13 21:24:59 +00:00
2015-10-01 13:08:34 +00:00
#################################################################################################
2015-05-07 06:11:20 +00:00
2015-03-13 21:24:59 +00:00
class PlayUtils ( ) :
2015-05-07 06:11:20 +00:00
clientInfo = ClientInformation ( )
addonName = clientInfo . getAddonName ( )
def logMsg ( self , msg , lvl = 1 ) :
className = self . __class__ . __name__
2015-06-15 18:42:34 +00:00
utils . logMsg ( " %s %s " % ( self . addonName , className ) , msg , int ( lvl ) )
2015-05-07 06:11:20 +00:00
2015-08-21 01:51:54 +00:00
def getPlayUrl ( self , server , id , result ) :
2015-04-13 18:56:36 +00:00
2015-07-18 08:08:05 +00:00
if self . isDirectPlay ( result , True ) :
# Try direct play
playurl = self . directPlay ( result )
if playurl :
self . logMsg ( " File is direct playing. " , 1 )
2015-10-01 13:08:34 +00:00
utils . window ( " %s playmethod " % playurl . encode ( ' utf-8 ' ) , value = " DirectPlay " )
2015-05-07 06:11:20 +00:00
2015-07-23 07:30:04 +00:00
elif self . isDirectStream ( result ) :
2015-07-18 08:08:05 +00:00
# Try direct stream
2015-08-21 01:51:54 +00:00
playurl = self . directStream ( result , server , id )
2015-07-18 08:08:05 +00:00
if playurl :
self . logMsg ( " File is direct streaming. " , 1 )
2015-10-01 13:08:34 +00:00
utils . window ( " %s playmethod " % playurl , value = " DirectStream " )
2015-07-18 08:08:05 +00:00
2015-07-23 08:18:17 +00:00
elif self . isTranscoding ( result ) :
# Try transcoding
2015-07-18 08:08:05 +00:00
playurl = self . transcoding ( result , server , id )
if playurl :
2015-05-07 06:11:20 +00:00
self . logMsg ( " File is transcoding. " , 1 )
2015-10-01 13:08:34 +00:00
utils . window ( " %s playmethod " % playurl , value = " Transcode " )
else : # Error
utils . window ( " playurlFalse " , value = " true " )
return
2015-05-07 06:11:20 +00:00
2015-05-10 12:39:16 +00:00
return playurl . encode ( ' utf-8 ' )
2015-05-07 06:11:20 +00:00
2015-10-01 13:08:34 +00:00
def isDirectPlay ( self , result , dialog = False ) :
2015-05-07 06:11:20 +00:00
# Requirements for Direct play:
# FileSystem, Accessible path
2015-10-01 13:08:34 +00:00
if utils . settings ( ' playFromStream ' ) == " true " :
# User forcing to play via HTTP instead of SMB
2015-06-02 21:26:23 +00:00
self . logMsg ( " Can ' t direct play: Play from HTTP is enabled. " , 1 )
2015-05-07 06:11:20 +00:00
return False
2015-12-02 10:58:20 +00:00
# Avoid H265 1080p
if result [ ' MediaSources ' ] [ 0 ] [ ' Name ' ] . startswith ( " 1080P/H265 " ) :
self . logMsg ( " Skip direct play for 1080P/H265 since format playback is not stable. " , 1 )
return False
2015-10-01 13:08:34 +00:00
canDirectPlay = result [ ' MediaSources ' ] [ 0 ] [ ' SupportsDirectPlay ' ]
2015-05-07 06:11:20 +00:00
# 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 )
2015-05-07 06:11:20 +00:00
return False
2015-10-01 13:08:34 +00:00
location = result [ ' LocationType ' ]
2015-05-07 06:11:20 +00:00
# File needs to be "FileSystem"
2015-10-01 13:08:34 +00:00
if ' FileSystem ' in location :
2015-05-07 06:11:20 +00:00
# Verify if path is accessible
if self . fileExists ( result ) :
return True
2015-05-08 00:38:27 +00:00
else :
2015-09-30 23:19:34 +00:00
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 add-on settings. " % result [ ' MediaSources ' ] [ 0 ] [ ' Path ' ] , 1 )
2015-07-18 08:08:05 +00:00
if dialog :
2015-09-30 23:19:34 +00:00
failCount = int ( utils . settings ( ' directSteamFailedCount ' ) )
self . logMsg ( " Direct Play failCount: %s . " % failCount , 1 )
if failCount < 2 :
# Let user know that direct play failed
utils . settings ( ' directSteamFailedCount ' , value = str ( failCount + 1 ) )
xbmcgui . Dialog ( ) . notification ( " Emby server " , " Unable to direct play. Verify your log for more information. " , icon = " special://home/addons/plugin.video.emby/icon.png " , sound = False )
elif utils . settings ( ' playFromStream ' ) != " true " :
# Permanently set direct stream as true
utils . settings ( ' playFromStream ' , value = " true " )
xbmcgui . Dialog ( ) . notification ( " Emby server " , " Enabled play from HTTP in add-on settings. " , icon = " special://home/addons/plugin.video.emby/icon.png " , sound = False )
2015-05-08 00:38:27 +00:00
return False
2015-05-07 06:11:20 +00:00
def directPlay ( self , result ) :
try :
2015-10-01 13:08:34 +00:00
playurl = result [ ' MediaSources ' ] [ 0 ] [ ' Path ' ]
except KeyError :
playurl = result [ ' Path ' ]
if ' VideoType ' in result :
# Specific format modification
if ' Dvd ' in result [ ' VideoType ' ] :
playurl = " %s /VIDEO_TS/VIDEO_TS.IFO " % playurl
elif ' BluRay ' in result [ ' VideoType ' ] :
playurl = " %s /BDMV/index.bdmv " % playurl
# Network - SMB protocol
if " \\ \\ " in playurl :
smbuser = utils . settings ( ' smbusername ' )
smbpass = utils . settings ( ' 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
2015-05-10 12:39:16 +00:00
2015-05-07 06:11:20 +00:00
def isDirectStream ( self , result ) :
# Requirements for Direct stream:
# FileSystem or Remote, BitRate, supported encoding
2015-12-02 10:58:20 +00:00
# Avoid H265 1080p
2015-12-02 11:00:00 +00:00
if result [ ' MediaSources ' ] [ 0 ] [ ' Name ' ] . startswith ( " 1080P/H265 " ) :
2015-12-02 10:58:20 +00:00
self . logMsg ( " Skip direct stream for 1080P/H265 since format playback is not stable. " , 1 )
return False
2015-12-02 11:00:00 +00:00
2015-10-01 13:08:34 +00:00
canDirectStream = result [ ' MediaSources ' ] [ 0 ] [ ' SupportsDirectStream ' ]
2015-05-07 06:11:20 +00:00
# Make sure it's supported by server
if not canDirectStream :
return False
2015-10-01 13:08:34 +00:00
location = result [ ' LocationType ' ]
2015-05-07 06:11:20 +00:00
# File can be FileSystem or Remote, not Virtual
2015-10-01 13:08:34 +00:00
if ' Virtual ' in location :
2015-07-23 09:02:41 +00:00
self . logMsg ( " File location is virtual. Can ' t proceed. " , 1 )
2015-05-07 06:11:20 +00:00
return False
# Verify BitRate
if not self . isNetworkQualitySufficient ( result ) :
2015-07-23 09:02:41 +00:00
self . logMsg ( " The network speed is insufficient to playback the file. " , 1 )
2015-05-07 06:11:20 +00:00
return False
return True
2015-06-11 05:46:47 +00:00
2015-07-18 08:08:05 +00:00
def directStream ( self , result , server , id , type = " Video " ) :
2015-09-17 21:00:58 +00:00
if result [ ' Path ' ] . endswith ( ' .strm ' ) :
# Allow strm loading when direct streaming
playurl = self . directPlay ( result )
return playurl
2015-03-13 21:24:59 +00:00
2015-10-01 13:08:34 +00:00
if " ThemeVideo " in type :
playurl = " %s /mediabrowser/Videos/ %s /stream?static=true " % ( server , id )
elif " Video " in type :
playurl = " %s /mediabrowser/Videos/ %s /stream?static=true " % ( server , id )
elif " Audio " in type :
playurl = " %s /mediabrowser/Audio/ %s /stream.mp3 " % ( server , id )
return playurl
2015-08-21 01:51:54 +00:00
2015-05-07 06:11:20 +00:00
def isTranscoding ( self , result ) :
# Last resort, no requirements
# BitRate
2015-10-01 13:08:34 +00:00
canTranscode = result [ ' MediaSources ' ] [ 0 ] [ ' SupportsTranscoding ' ]
2015-05-07 06:11:20 +00:00
# Make sure it's supported by server
if not canTranscode :
return False
2015-10-01 13:08:34 +00:00
location = result [ ' LocationType ' ]
2015-05-07 06:11:20 +00:00
# File can be FileSystem or Remote, not Virtual
2015-10-01 13:08:34 +00:00
if ' Virtual ' in location :
2015-05-07 06:11:20 +00:00
return False
return True
def transcoding ( self , result , server , id ) :
2015-09-17 21:00:58 +00:00
if result [ ' Path ' ] . endswith ( ' .strm ' ) :
# Allow strm loading when transcoding
playurl = self . directPlay ( result )
return playurl
2015-05-07 06:11:20 +00:00
2015-10-01 13:08:34 +00:00
# Play transcoding
deviceId = self . clientInfo . getMachineId ( )
playurl = " %s /mediabrowser/Videos/ %s /master.m3u8?mediaSourceId= %s " % ( server , id , id )
playurl = " %s &VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId= %s &VideoBitrate= %s " % ( playurl , deviceId , self . getVideoBitRate ( ) * 1000 )
2015-03-13 21:24:59 +00:00
2015-10-01 13:08:34 +00:00
playurl = self . audioSubsPref ( playurl , result . get ( ' MediaSources ' ) )
self . logMsg ( " Playurl: %s " % playurl , 1 )
2015-05-07 06:11:20 +00:00
2015-10-01 13:08:34 +00:00
return playurl
def isNetworkQualitySufficient ( self , result ) :
# Works out if the network quality can play directly or if transcoding is needed
2015-03-13 21:24:59 +00:00
settingsVideoBitRate = self . getVideoBitRate ( )
2015-05-07 06:11:20 +00:00
settingsVideoBitRate = settingsVideoBitRate * 1000
try :
2015-10-01 13:08:34 +00:00
mediaSources = result [ ' MediaSources ' ]
sourceBitRate = int ( mediaSources [ 0 ] [ ' Bitrate ' ] )
except KeyError :
self . logMsg ( " Bitrate value is missing. " , 1 )
2015-08-08 15:11:41 +00:00
else :
2015-08-08 13:15:05 +00:00
self . logMsg ( " The video quality selected is: %s , the video bitrate required to direct stream is: %s . " % ( settingsVideoBitRate , sourceBitRate ) , 1 )
2015-08-08 15:11:41 +00:00
if settingsVideoBitRate < sourceBitRate :
2015-05-07 06:11:20 +00:00
return False
2015-08-08 15:11:41 +00:00
return True
2015-03-13 21:24:59 +00:00
def getVideoBitRate ( self ) :
2015-05-07 06:11:20 +00:00
# get the addon video quality
2015-08-14 09:03:12 +00:00
videoQuality = utils . settings ( ' videoBitRate ' )
2015-10-01 13:08:34 +00:00
bitrate = {
' 0 ' : 664 ,
' 1 ' : 996 ,
' 2 ' : 1320 ,
' 3 ' : 2000 ,
' 4 ' : 3200 ,
' 5 ' : 4700 ,
' 6 ' : 6200 ,
' 7 ' : 7700 ,
' 8 ' : 9200 ,
' 9 ' : 10700 ,
' 10 ' : 12200 ,
' 11 ' : 13700 ,
' 12 ' : 15200 ,
' 13 ' : 16700 ,
' 14 ' : 18200 ,
' 15 ' : 20000 ,
' 16 ' : 40000 ,
' 17 ' : 100000 ,
' 18 ' : 1000000
}
# max bit rate supported by server (max signed 32bit integer)
return bitrate . get ( videoQuality , 2147483 )
2015-03-14 03:09:36 +00:00
2015-03-13 21:24:59 +00:00
def fileExists ( self , result ) :
2015-05-07 06:11:20 +00:00
2015-10-01 13:08:34 +00:00
if ' Path ' not in result :
2015-05-07 06:11:20 +00:00
# File has no path in server
2015-05-05 02:53:21 +00:00
return False
2015-05-07 06:11:20 +00:00
2015-06-06 06:06:33 +00:00
# Convert Emby path to a path we can verify
path = self . directPlay ( result )
2015-06-09 10:30:01 +00:00
2015-05-17 10:52:21 +00:00
try :
2015-06-06 06:06:33 +00:00
pathexists = xbmcvfs . exists ( path )
except :
pathexists = False
2015-05-07 06:11:20 +00:00
# Verify the device has access to the direct path
2015-05-17 11:27:28 +00:00
if pathexists :
2015-05-17 01:21:49 +00:00
# Local or Network path
self . logMsg ( " Path exists. " , 2 )
2015-03-13 21:24:59 +00:00
return True
2015-07-25 11:57:39 +00:00
elif " : " not in path :
# Give benefit of the doubt for nfs.
self . logMsg ( " Can ' t verify path (assumed NFS). Still try direct play. " , 2 )
2015-05-07 21:33:50 +00:00
return True
2015-03-13 21:24:59 +00:00
else :
2015-06-06 06:06:33 +00:00
self . logMsg ( " Path is detected as follow: %s . Try direct streaming. " % path , 2 )
2015-10-01 13:08:34 +00:00
return False
def audioSubsPref ( self , url , mediaSources ) :
# For transcoding only
# Present the list of audio to select from
audioStreamsList = { }
audioStreams = [ ]
audioStreamsChannelsList = { }
subtitleStreamsList = { }
subtitleStreams = [ ' No subtitles ' ]
selectAudioIndex = " "
selectSubsIndex = " "
playurlprefs = " %s " % url
mediaStream = mediaSources [ 0 ] . get ( ' MediaStreams ' )
for stream in mediaStream :
# Since Emby returns all possible tracks together, have to sort them.
index = stream [ ' Index ' ]
type = stream [ ' Type ' ]
if ' Audio ' in type :
codec = stream [ ' Codec ' ]
channelLayout = stream [ ' ChannelLayout ' ]
try :
track = " %s - %s - %s %s " % ( index , stream [ ' Language ' ] , codec , channelLayout )
except :
track = " %s - %s %s " % ( index , codec , channelLayout )
audioStreamsChannelsList [ index ] = stream [ ' Channels ' ]
audioStreamsList [ track ] = index
audioStreams . append ( track )
elif ' Subtitle ' in type :
try :
track = " %s - %s " % ( index , stream [ ' Language ' ] )
except :
track = " %s - %s " % ( index , stream [ ' Codec ' ] )
default = stream [ ' IsDefault ' ]
forced = stream [ ' IsForced ' ]
if default :
track = " %s - Default " % track
if forced :
track = " %s - Forced " % track
subtitleStreamsList [ track ] = index
subtitleStreams . append ( track )
if len ( audioStreams ) > 1 :
resp = xbmcgui . Dialog ( ) . select ( " Choose the audio stream " , audioStreams )
if resp > - 1 :
# User selected audio
selected = audioStreams [ resp ]
selectAudioIndex = audioStreamsList [ selected ]
playurlprefs + = " &AudioStreamIndex= %s " % selectAudioIndex
else : # User backed out of selection
playurlprefs + = " &AudioStreamIndex= %s " % mediaSources [ 0 ] [ ' DefaultAudioStreamIndex ' ]
else : # There's only one audiotrack.
selectAudioIndex = audioStreamsList [ audioStreams [ 0 ] ]
playurlprefs + = " &AudioStreamIndex= %s " % selectAudioIndex
if len ( subtitleStreams ) > 1 :
resp = xbmcgui . Dialog ( ) . select ( " Choose the subtitle stream " , subtitleStreams )
if resp == 0 :
# User selected no subtitles
pass
elif resp > - 1 :
# User selected subtitles
selected = subtitleStreams [ resp ]
selectSubsIndex = subtitleStreamsList [ selected ]
playurlprefs + = " &SubtitleStreamIndex= %s " % selectSubsIndex
else : # User backed out of selection
playurlprefs + = " &SubtitleStreamIndex= %s " % mediaSources [ 0 ] . get ( ' DefaultSubtitleStreamIndex ' , " " )
# Get number of channels for selected audio track
audioChannels = audioStreamsChannelsList . get ( selectAudioIndex , 0 )
if audioChannels > 2 :
playurlprefs + = " &AudioBitrate=384000 "
else :
playurlprefs + = " &AudioBitrate=192000 "
return playurlprefs