# -*- coding: utf-8 -*- ################################################################################################# # utils class ################################################################################################# import xbmc import xbmcgui import xbmcaddon import xbmcvfs from ClientInformation import ClientInformation import Utils as utils ########################################################################### class PlayUtils(): _shared_state = {} clientInfo = ClientInformation() addonName = clientInfo.getAddonName() addon = xbmcaddon.Addon() WINDOW = xbmcgui.Window(10000) audioPref = addon.getSetting('Audiopref') subsPref = addon.getSetting('Subspref') 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), msg, int(lvl)) def getPlayUrl(self, server, id, result): WINDOW = self.WINDOW username = WINDOW.getProperty('currUser') server = WINDOW.getProperty('server%s' % username) if self.isDirectPlay(result,True): # Try direct play playurl = self.directPlay(result) if playurl: self.logMsg("File is direct playing.", 1) WINDOW.setProperty("%splaymethod" % playurl.encode('utf-8'), "DirectPlay") elif self.isDirectStream(result): # Try direct stream playurl = self.directStream(result, server, id) if playurl: self.logMsg("File is direct streaming.", 1) WINDOW.setProperty("%splaymethod" % playurl, "DirectStream") elif self.isTranscoding(result): # Try transcoding playurl = self.transcoding(result, server, id) if playurl: self.logMsg("File is transcoding.", 1) WINDOW.setProperty("%splaymethod" % playurl, "Transcode") else: # Error return False return playurl.encode('utf-8') def isDirectPlay(self, result, dialog=False): # Requirements for Direct play: # FileSystem, Accessible path self.addon = xbmcaddon.Addon() playhttp = self.addon.getSetting('playFromStream') # User forcing to play via HTTP instead of SMB if playhttp == "true": 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: 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 else: 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']) if dialog: # Let user know that direct play failed dialog = xbmcgui.Dialog() resp = dialog.select('Warning: Unable to direct play.', ['Play from HTTP', 'Play from HTTP and remember next time.']) if resp == 1: # Remember next time self.addon.setSetting('playFromStream', "true") elif resp < 0: # User decided not to proceed. self.logMsg("User cancelled HTTP selection dialog.", 1) self.WINDOW.setProperty("playurlFalse", "true") return False def directPlay(self, result): addon = self.addon try: try: playurl = result[u'MediaSources'][0][u'Path'] except: playurl = result[u'Path'] except: self.logMsg("Direct play failed. Trying Direct stream.", 1) return False else: 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 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: self.logMsg("File location is virtual. Can't proceed.", 1) return False # Verify BitRate if not self.isNetworkQualitySufficient(result): self.logMsg("The network speed is insufficient to playback the file.", 1) return False return True def directStream(self, result, server, id, type = "Video"): try: 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) # Verify audio and subtitles 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')) elif "Audio" in type: playurl = "%s/mediabrowser/Audio/%s/stream.mp3" % (server, id) 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() 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) self.logMsg("Playurl: %s" % playurl) return playurl except: self.logMsg("Transcoding failed.") return False # Works out if the network quality can play directly or if transcoding is needed def isNetworkQualitySufficient(self, result): settingsVideoBitRate = self.getVideoBitRate() settingsVideoBitRate = settingsVideoBitRate * 1000 try: mediaSources = result[u'MediaSources'] sourceBitRate = int(mediaSources[0][u'Bitrate']) self.logMsg("The video quality selected is: %s, the video bitrate required to direct stream is: %s." % (settingsVideoBitRate, sourceBitRate), 1) if settingsVideoBitRate > sourceBitRate: return True else: return False except: return True def getVideoBitRate(self): # get the addon video quality videoQuality = self.addon.getSetting('videoBitRate') if (videoQuality == "0"): return 664 elif (videoQuality == "1"): return 996 elif (videoQuality == "2"): return 1320 elif (videoQuality == "3"): return 2000 elif (videoQuality == "4"): return 3200 elif (videoQuality == "5"): return 4700 elif (videoQuality == "6"): return 6200 elif (videoQuality == "7"): return 7700 elif (videoQuality == "8"): return 9200 elif (videoQuality == "9"): return 10700 elif (videoQuality == "10"): return 12200 elif (videoQuality == "11"): return 13700 elif (videoQuality == "12"): return 15200 elif (videoQuality == "13"): return 16700 elif (videoQuality == "14"): return 18200 elif (videoQuality == "15"): return 20000 elif (videoQuality == "16"): return 40000 elif (videoQuality == "17"): return 100000 elif (videoQuality == "18"): return 1000000 else: return 2147483 # max bit rate supported by server (max signed 32bit integer) def fileExists(self, result): if u'Path' not in result: # File has no path in server return False # Convert Emby path to a path we can verify path = self.directPlay(result) if not path: return False try: pathexists = xbmcvfs.exists(path) except: pathexists = False # Verify the device has access to the direct path if pathexists: # Local or Network path self.logMsg("Path exists.", 2) return True elif ":" not in path: # Give benefit of the doubt for nfs. self.logMsg("Can't verify path (assumed NFS). Still try direct play.", 2) return True else: self.logMsg("Path is detected as follow: %s. Try direct streaming." % path, 2) return False