def _get_show_streams(self, stream_data, show, episode, platform="desktop"): video_id = parse_json(stream_data.group(1), schema=self.vod_id_schema) res = http.get(self.vod_api, params={ "platform": platform, "id": video_id }) # create a unique list of the stream manifest URLs streams = [] urldups = [] for stream in parse_xml(res.text, schema=self._vod_api_schema): if stream["url"] not in urldups: streams.append(stream) urldups.append(stream["url"]) mapper = StreamMapper(lambda fmt, strm: strm["url"].endswith(fmt)) mapper.map(".m3u8", self._make_hls_hds_stream, HLSStream.parse_variant_playlist) mapper.map(".f4m", self._make_hls_hds_stream, HDSStream.parse_manifest, is_akamai=True) mapper.map( ".mp4", lambda s: (s["bitrate"] + "k", HTTPStream(self.session, s["url"]))) for q, s in mapper(streams): yield q, s
def _get_live_streams(self, player): mappers = [] swf_url = SWF_URL for playlist in player.get("playlist", []): bitrates = playlist.get("bitrates") provider = playlist.get("connectionProvider") rtmp = None if bitrates: rtmp = playlist.get("netConnectionUrl") elif provider and provider in player["plugins"]: provider = player["plugins"][provider] swf_name = provider["url"] swf_url = SWF_BASE + swf_name rtmp = provider["netConnectionUrl"] bitrates = player["clip"]["bitrates"] else: continue mapper = StreamMapper(cmp=lambda provider, bitrate: bitrate[ "provider"].startswith(provider)) mapper.map("hls", self._create_hls_streams) mapper.map("rtmp", self._create_rtmp_stream, rtmp, swf_url) mappers.append(mapper(bitrates)) return chain.from_iterable(mappers)
def _get_video_streams(self, player): base_url = player["clip"]["baseUrl"] or VOD_BASE_URL mapper = StreamMapper(cmp=lambda ext, bitrate: urlparse(bitrate["url"]) .path.endswith(ext)) mapper.map(".m3u8", self._create_video_stream, HLSStream, base_url) mapper.map(".mp4", self._create_video_stream, HTTPStream, base_url) mapper.map(".flv", self._create_video_stream, HTTPStream, base_url) return mapper(player["clip"]["bitrates"])
def _extract_streams(self, stream_id): res = http.get(STREAM_API_URL.format(stream_id), raise_for_status=False) stream_info = http.json(res, schema=_stream_schema) if stream_info.get("msg"): # error message self.logger.error(stream_info.get("msg")) raise NoStreamsError(self.url) mapper = StreamMapper( lambda pattern, video: re.search(pattern, video[1])) mapper.map(r"/\w+\.m3u8", self._create_dynamic_streams, "HLS", HLSStream.parse_variant_playlist) mapper.map(r"/\w+\.f4m", self._create_dynamic_streams, "HDS", HDSStream.parse_manifest) mapper.map(r"^rtmp://", self._create_rtmp_stream) return mapper(stream_info.items())
def _get_live_stream(self, stream_data, show, episode=None): # parse the stream info as json stream_info = parse_json(stream_data.group(1), schema=self.live_schema) # get the stream ID stream_id = None show_info = stream_info[u"streams"][show] if episode: self.logger.debug("Loading replay of episode: {0}/{1}", show, episode) for epi in show_info[u"archiveEpisodes"]: if epi[u"slug"] == episode: stream_id = epi[u"id"] elif show_info.get("isLive") or not len(show_info[u"archiveEpisodes"]): self.logger.debug("Loading LIVE streams for: {0}", show) stream_id = show_info[u"stream"] else: # off-air if len(show_info[u"archiveEpisodes"]): epi = show_info[u"archiveEpisodes"][0] self.logger.debug("Loading replay of episode: {0}/{1}", show, epi[u"slug"]) stream_id = epi[u"id"] else: self.logger.error("This stream is currently offline") return if stream_id: api_url = self.API_URL.format(id=stream_id) res = http.get(api_url, headers={"User-Agent": useragents.SAFARI_8}) stream_data = http.json(res, schema=self._api_schema) mapper = StreamMapper(lambda fmt, surl: surl.endswith(fmt)) mapper.map(".m3u8", HLSStream.parse_variant_playlist, self.session) mapper.map(".f4m", HDSStream.parse_manifest, self.session) stream_urls = [ asset[u"url"] for asset in stream_data[u'data'][u'stream'][u'assets'] ] for q, s in mapper(stream_urls): yield q, s else: self.logger.error( "Couldn't find the stream ID for this stream: {0}".format( show))
def _get_streams(self): # Retrieve URL page and search for new type of video ID res = http.get(self.url) match = _id_re.search(res.text) # Use API if match, otherwise resort to old method if match: vid = match.group("id") res = http.get(API_URL.format(vid)) videos = http.json(res, schema=_video_schema) mapper = StreamMapper( cmp=lambda format, video: video["format"] == format) mapper.map("hls", self._create_streams, "HLS", HLSStream.parse_variant_playlist) mapper.map("hds", self._create_streams, "HDS", HDSStream.parse_manifest) else: res = http.get(self.url, params=dict(output="json")) videos = http.json(res, schema=_old_video_schema) mapper = StreamMapper( cmp=lambda type, video: video["playerType"] == type) mapper.map("ios", self._create_streams, "HLS", HLSStream.parse_variant_playlist) mapper.map("flash", self._create_streams, "HDS", HDSStream.parse_manifest) return mapper(videos)
def _get_streams(self): # Get domain name self.domain = _url_re.match(self.url).group('domain') # Set header data for user-agent hdr = {'User-Agent': USER_AGENT.format('sv_SE')} # Parse video ID from data received from supplied URL res = http.get(self.url, headers=hdr).text match = _videoid_re.search(res) if not match: # Video ID not found self.logger.error('Failed to parse video ID') return {} videoId = match.group('id') # Get data from general API to validate that stream is playable res = http.get(GENERAL_API_URL.format(self.domain, videoId), headers=hdr) data = http.json(res, schema=_api_schema) if not data['data']: # No data item found self.logger.error('Unable to find "data" item in general API response') return {} if not self._is_playable(data): # Stream not playable self.logger.error('Stream is not playable (Premium or DRM-protected content)') return {} # Get geo data, validate and form cookie consisting of # geo data + expiry timestamp (current time + 1 hour) res = http.get(GEO_DATA_URL.format(self.domain), headers=hdr) geo = http.json(res, schema=_geo_schema) timestamp = (int(time.time()) + 3600) * 1000 cookie = 'dsc-geo=%s' % quote('{"countryCode":"%s","expiry":%s}' % (geo, timestamp)) # Append cookie to headers hdr['Cookie'] = cookie # Get available streams using stream API try: res = http.get(STREAM_API_URL.format(self.domain, videoId, 'hls'), headers=hdr, verify=False) data = http.json(res, schema=_media_schema) media = data.copy() res = http.get(STREAM_API_URL.format(self.domain, videoId, 'hds'), headers=hdr, verify=False) data = http.json(res, schema=_media_schema) media.update(data) except PluginError as err: # Likely geo-restricted if any(e in str(err) for e in ('401 Client Error', '403 Client Error')): self.logger.error('Failed to access stream API, ' 'may be due to geo-restriction') raise NoStreamsError(self.url) else: raise # Reformat data into list with stream format and url streams = [{'format': k, 'url': media[k]} for k in media] # Create mapper for supported stream types (HLS/HDS) mapper = StreamMapper(cmp=lambda type, video: video['format'] == type) mapper.map('hls', self._create_streams, HLSStream.parse_variant_playlist) mapper.map('hds', self._create_streams, HDSStream.parse_manifest) # Feed stream data to mapper and return all streams found return mapper(streams)