# -*- coding: utf-8 -*- ################################################################################################# import xbmc import xbmcgui import xbmcvfs import clientinfo import utils ################################################################################################# class PlayUtils(): def __init__(self, item): self.item = item self.clientInfo = clientinfo.ClientInfo() self.addonName = self.clientInfo.getAddonName() self.userid = utils.window('emby_currUser') self.server = utils.window('emby_server%s' % self.userid) def logMsg(self, msg, lvl=1): self.className = self.__class__.__name__ utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl) def getPlayUrl(self): item = self.item playurl = None if item['MediaSources'][0]['Protocol'] == "Http": # Only play as http self.logMsg("File protocol is http.", 1) playurl = self.httpPlay() utils.window('emby_%s.playmethod' % playurl, value="DirectStream") elif self.isDirectPlay(): self.logMsg("File is direct playing.", 1) playurl = self.directPlay() playurl = playurl.encode('utf-8') # Set playmethod property utils.window('emby_%s.playmethod' % playurl, value="DirectPlay") elif self.isDirectStream(): self.logMsg("File is direct streaming.", 1) playurl = self.directStream() # Set playmethod property utils.window('emby_%s.playmethod' % playurl, value="DirectStream") elif self.isTranscoding(): self.logMsg("File is transcoding.", 1) playurl = self.transcoding() # Set playmethod property utils.window('emby_%s.playmethod' % playurl, value="Transcode") return playurl def httpPlay(self): # Audio, Video, Photo item = self.item server = self.server itemid = item['Id'] mediatype = item['MediaType'] if type == "Audio": playurl = "%s/emby/Audio/%s/stream" % (server, itemid) else: playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid) return playurl def isDirectPlay(self): item = self.item # Requirement: Filesystem, Accessible path if utils.settings('playFromStream') == "true": # User forcing to play via HTTP self.logMsg("Can't direct play, play from HTTP enabled.", 1) return False if (utils.settings('transcodeH265') == "true" and item['MediaSources'][0]['Name'].startswith("1080P/H265")): # Avoid H265 1080p self.logMsg("Option to transcode 1080P/H265 enabled.", 1) return False canDirectPlay = item['MediaSources'][0]['SupportsDirectPlay'] # Make sure direct play is supported by the server if not canDirectPlay: self.logMsg("Can't direct play, server doesn't allow/support it.", 1) return False location = item['LocationType'] if location == "FileSystem": # Verify the path if not self.fileExists(): self.logMsg("Unable to direct play.") try: count = int(utils.settings('failCount')) except ValueError: count = 0 self.logMsg("Direct play failed: %s times." % count, 1) if count < 2: # Let the user know that direct play failed utils.settings('failCount', value=str(count+1)) xbmcgui.Dialog().notification( heading="Emby server", message="Unable to direct play.", icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", sound=False) elif utils.settings('playFromStream') != "true": # Permanently set direct stream as true utils.settings('playFromStream', value="true") utils.settings('failCount', value="0") xbmcgui.Dialog().notification( heading="Emby server", message=("Direct play failed 3 times. Enabled play " "from HTTP in the add-on settings."), icon="special://home/addons/plugin.video.plexkodiconnect/icon.png", sound=False) return False return True def directPlay(self): item = self.item try: playurl = item['MediaSources'][0]['Path'] except (IndexError, KeyError): playurl = item['Path'] if item.get('VideoType'): # Specific format modification type = item['VideoType'] if type == "Dvd": playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl elif type == "Bluray": playurl = "%s/BDMV/index.bdmv" % playurl # Assign network protocol if playurl.startswith('\\\\'): 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 fileExists(self): if 'Path' not in self.item: # File has no path defined in server return False # Convert path to direct play path = self.directPlay() self.logMsg("Verifying path: %s" % path, 1) if xbmcvfs.exists(path): self.logMsg("Path exists.", 1) return True elif ":" not in path: self.logMsg("Can't verify path, assumed linux. Still try to direct play.", 1) return True else: self.logMsg("Failed to find file.") return False def isDirectStream(self): item = self.item if (utils.settings('transcodeH265') == "true" and result['MediaSources'][0]['Name'].startswith("1080P/H265")): # Avoid H265 1080p self.logMsg("Option to transcode 1080P/H265 enabled.", 1) return False # Requirement: BitRate, supported encoding canDirectStream = item['MediaSources'][0]['SupportsDirectStream'] # Make sure the server supports it if not canDirectStream: return False # Verify the bitrate if not self.isNetworkSufficient(): self.logMsg("The network speed is insufficient to direct stream file.", 1) return False return True def directStream(self): item = self.item server = self.server itemid = item['Id'] type = item['Type'] if 'Path' in item and item['Path'].endswith('.strm'): # Allow strm loading when direct streaming playurl = self.directPlay() elif type == "Audio": playurl = "%s/emby/Audio/%s/stream.mp3" % (server, itemid) else: playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid) return playurl def isNetworkSufficient(self): settings = self.getBitrate()*1000 try: sourceBitrate = int(self.item['MediaSources'][0]['Bitrate']) except (KeyError, TypeError): self.logMsg("Bitrate value is missing.", 1) else: self.logMsg("The add-on settings bitrate is: %s, the video bitrate required is: %s" % (settings, sourceBitrate), 1) if settings < sourceBitrate: return False return True def isTranscoding(self): item = self.item canTranscode = item['MediaSources'][0]['SupportsTranscoding'] # Make sure the server supports it if not canTranscode: return False return True def transcoding(self): item = self.item if 'Path' in item and item['Path'].endswith('.strm'): # Allow strm loading when transcoding playurl = self.directPlay() else: itemid = item['Id'] deviceId = self.clientInfo.getDeviceId() playurl = ( "%s/emby/Videos/%s/master.m3u8?MediaSourceId=%s" % (self.server, itemid, itemid) ) playurl = ( "%s&VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId=%s&VideoBitrate=%s" % (playurl, deviceId, self.getBitrate()*1000)) return playurl def getBitrate(self): # get the addon video quality videoQuality = utils.settings('videoBitrate') 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) def audioSubsPref(self, url): # For transcoding only # Present the list of audio to select from audioStreamsList = {} audioStreams = [] audioStreamsChannelsList = {} subtitleStreamsList = {} subtitleStreams = ['No subtitles'] selectAudioIndex = "" selectSubsIndex = "" playurlprefs = "%s" % url item = self.item try: mediasources = item['MediaSources'][0] mediastreams = mediasources['MediaStreams'] except (TypeError, KeyError, IndexError): return for stream in mediastreams: # 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.get('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: if stream['IsExternal']: continue 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['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.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