def downloadWebData(self, url, headers=None): for _ in range(MAX_RETRIES): try: req = Request(url) req.add_header(Keys.USER_AGENT, Keys.USER_AGENT_STRING) if headers: for key, value in headers.iteritems(): req.add_header(key, value) response = urlopen(req) if sys.version_info < (3, 0): data = response.read().decode('utf-8') else: data = response.readall().decode('utf-8') response.close() break except Exception as err: if not isinstance(err, URLError): self.logger.debug("Error %s during HTTP Request, abort", repr(err)) raise # propagate non-URLError self.logger.debug("Error %s during HTTP Request, retrying", repr(err)) else: raise TwitchException(TwitchException.HTTP_ERROR) return data
def getJson(self, url, headers=None): def getClientID(): # return a Client ID to use for Twitch API client_id = Addon().getSetting( 'oauth_client_id') # get from settings if not client_id: # not in settings try: client_id = b64decode(Keys.CLIENT_ID) # use Keys.CLIENT_ID except: client_id = '' return client_id if not headers: headers = {} # Set api version and client id headers headers.setdefault(Keys.ACCEPT, Keys.API_VERSION) headers.setdefault(Keys.CLIENT_ID_HEADER, getClientID()) jsonString = self.downloadWebData(url, headers) try: jsonDict = json.loads(jsonString) self.logger.debug(json.dumps(jsonDict, indent=4, sort_keys=True)) return jsonDict except: raise TwitchException(TwitchException.JSON_ERROR)
def __init__(self, data, qualityList=None): self.playlist = dict() self.qualityList = qualityList or Keys.QUALITY_LIST_STREAM def parseQuality(ExtXMediaLine, ExtXStreamInfLine, Url): # find name of current quality, NAME=", 6 chars namePosition = ExtXMediaLine.find('NAME') if namePosition == -1: raise TwitchException(TwitchException.NO_PLAYABLE) qualityString = '' namePosition += 6 for char in ExtXMediaLine[namePosition:]: if char == '"': break qualityString += char return qualityString, Url lines = data.splitlines() linesIterator = iter(lines) for line in linesIterator: if line.startswith('#EXT-X-MEDIA'): quality, url = parseQuality(line, next(linesIterator), next(linesIterator)) qualityInt = self.qualityList.index(quality) self.playlist[qualityInt] = url if not self.playlist: # playlist dict is empty raise TwitchException(TwitchException.NO_PLAYABLE)
def getStreamInfo(self, channelname): # Get Stream info url = ''.join([Urls.STREAMS, channelname]) stream = self._fetchItems(url, Keys.STREAM) if stream: return stream else: raise TwitchException(TwitchException.STREAM_OFFLINE)
def parseQuality(ExtXMediaLine, ExtXStreamInfLine, Url): # find name of current quality, NAME=", 6 chars namePosition = ExtXMediaLine.find('NAME') if namePosition == -1: raise TwitchException(TwitchException.NO_PLAYABLE) qualityString = '' namePosition += 6 for char in ExtXMediaLine[namePosition:]: if char == '"': break qualityString += char return qualityString, Url
def getLiveStream(self, channelName, maxQuality): # Get Access Token (not necessary at the moment but could come into effect at any time) tokenurl = Urls.CHANNEL_TOKEN.format(channelName) channeldata = self.scraper.getJson(tokenurl) channeltoken = channeldata[Keys.TOKEN] channelsig = channeldata[Keys.SIG] # Download and Parse Multiple Quality Stream Playlist try: hls_url = Urls.HLS_PLAYLIST.format(channelName, channelsig, channeltoken) data = self.scraper.downloadWebData(hls_url) playlist = M3UPlaylist(data) return playlist.getQuality(maxQuality) except TwitchException: # HTTP Error in download web data -> stream is offline raise TwitchException(TwitchException.STREAM_OFFLINE)
def playVideo(_id, quality): """ :param _id: string: video id :param quality: string: qualities[quality] 0 = Source, 1 = 1080p60, 2 = 1080p30, 3 = 720p60, 4 = 720p30, 5 = 540p30, 6 = 480p30, 7 = 360p30, 8 = 240p30, 9 = 144p30 -1 = Choose quality dialog * any other value for quality will use addon setting """ videoQuality = utils.getVideoQuality(quality) oauthtoken = utils.getOauthToken() if videoQuality != -1: # videoQuality == -1 if quality dialog was cancelled videoInfo = CONVERTER.getVideoInfo(TWITCHTV.getVideo(_id)) simplePlaylist = TWITCHTV.getVideoPlaylist(_id, videoQuality, oauthtoken) playlistItems = PLAYLIST_CONVERTER.convertToXBMCPlaylist(simplePlaylist, videoInfo.get('title', ''), videoInfo.get('thumbnail', '')) if playlistItems != (): (playlist, listItem) = playlistItems utils.play(listItem.get('path', ''), listItem) else: raise TwitchException(TwitchException.NO_PLAYABLE)
def __init__(self, data, qualityList=None): self.playlist = dict() self.qualityList = qualityList or Keys.QUALITY_LIST_STREAM def parseQuality(ExtXMediaLine, ExtXStreamInfLine, Url): # find name of current quality, NAME=", 6 chars namePosition = ExtXMediaLine.find('NAME') if namePosition == -1: raise TwitchException(TwitchException.NO_PLAYABLE) qualityString = '' namePosition += 6 for char in ExtXMediaLine[namePosition:]: if char == '"': break qualityString += char return qualityString, Url lines = data.splitlines() linesIterator = iter(lines) for line in linesIterator: if line.startswith('#EXT-X-MEDIA:TYPE=VIDEO'): quality, url = parseQuality(line, next(linesIterator), next(linesIterator)) # do coercing of qualities here if quality not in self.qualityList: # non-standard quality naming, attempt to coerce if quality.endswith('p'): # '1080p' quality += '30' # '1080p30' is in qualityList else: quality = quality.split( None, 1 ) # '1080p60 - source' -> ['1080p60', ' - source'] if quality: quality = quality[0] # '1080p60' is in qualityList if quality in self.qualityList: # check for quality in list before using it qualityInt = self.qualityList.index(quality) self.playlist[qualityInt] = url if not self.playlist: # playlist dict is empty raise TwitchException(TwitchException.NO_PLAYABLE)
def playVideo(_id, quality): """ :param _id: string: video id :param quality: string: qualities[quality] qualities = {'-1': -1, '0': 0, '1': 1, '2': 2, '3': 3, '4': 4} 0 = Best, 1 = 720, 2 = 480, 3 = 360, 4 = 226, -1 = Choose quality dialog * any other value for quality will use addon setting """ videoQuality = utils.getVideoQuality(quality) if videoQuality != -1: # videoQuality == -1 if quality dialog was cancelled videoInfo = CONVERTER.getVideoInfo(TWITCHTV.getVideo(_id)) simplePlaylist = TWITCHTV.getVideoPlaylist(_id, videoQuality) playlistItems = PLAYLIST_CONVERTER.convertToXBMCPlaylist( simplePlaylist, videoInfo.get('title', ''), videoInfo.get('thumbnail', '')) if playlistItems != (): (playlist, listItem) = playlistItems utils.play(listItem.get('path', ''), listItem) else: raise TwitchException(TwitchException.NO_PLAYABLE)
def parseQuality(ExtXMediaLine, ExtXStreamInfLine, Url): # find name of current quality, NAME=", 6 chars namePosition = ExtXMediaLine.find('NAME') if namePosition == -1: raise TwitchException(TwitchException.NO_PLAYABLE) qualityString = '' namePosition += 6 for char in ExtXMediaLine[namePosition:]: if char == '"': break qualityString += char groupPosition = ExtXMediaLine.find('GROUP-ID') if namePosition == -1: group_id = 'unknown' else: group_id = '' groupPosition += 10 for char in ExtXMediaLine[groupPosition:]: if char == '"': break group_id += char return group_id, qualityString, Url
def __init__(self, data, qualityList=None): self.playlist = dict() self.qualityList = qualityList or Keys.QUALITY_LIST_STREAM def parseQuality(ExtXMediaLine, ExtXStreamInfLine, Url): # find name of current quality, NAME=", 6 chars namePosition = ExtXMediaLine.find('NAME') if namePosition == -1: raise TwitchException(TwitchException.NO_PLAYABLE) qualityString = '' namePosition += 6 for char in ExtXMediaLine[namePosition:]: if char == '"': break qualityString += char groupPosition = ExtXMediaLine.find('GROUP-ID') if namePosition == -1: group_id = 'unknown' else: group_id = '' groupPosition += 10 for char in ExtXMediaLine[groupPosition:]: if char == '"': break group_id += char return group_id, qualityString, Url source = None lines = data.splitlines() linesIterator = iter(lines) qualityMap = {'high': 1, 'medium': 2, 'low': 3, 'mobile': 4} for line in linesIterator: if line.startswith('#EXT-X-MEDIA:TYPE=VIDEO'): group_id, quality, url = parseQuality(line, next(linesIterator), next(linesIterator)) if group_id == 'chunked': source = url # do coercing of qualities here if quality not in self.qualityList: # non-standard quality naming, attempt to coerce if quality.endswith('p'): # '1080p' quality += '30' # '1080p30' is in qualityList else: quality = quality.split( None, 1 ) # '1080p60 - source' -> ['1080p60', ' - source'] if quality: quality = quality[0] # '1080p60' is in qualityList if quality in self.qualityList: # check for quality in list before using it qualityInt = self.qualityList.index(quality) self.playlist[qualityInt] = url elif quality.lower( ) in qualityMap: # check for quality in list before using it qualityInt = qualityMap[quality.lower()] self.playlist[qualityInt] = url if (source) and (0 not in self.playlist): self.playlist[0] = source if not self.playlist: # playlist dict is empty raise TwitchException(TwitchException.NO_PLAYABLE)