def test_stream_open_video_only(self, muxer, reader): stream = DASHStream(self.session, Mock(), Mock(id=1, mimeType="video/mp4")) open_reader = reader.return_value = Mock() stream.open() reader.assert_called_with(stream, 1, "video/mp4") open_reader.open.assert_called_with() muxer.assert_not_called()
def _parse_streams(self, res): stream_url = validate.Schema( validate.parse_html(), validate.xml_xpath_string( ".//head/meta[@property='og:video:url'][@content][1]/@content") ).validate(res.text) if not stream_url: log.debug("No meta og:video:url") else: if ".mpd" in stream_url: for s in DASHStream.parse_manifest(self.session, stream_url).items(): yield s return elif ".mp4" in stream_url: yield "vod", HTTPStream(self.session, stream_url) return for match in self._src_re.finditer(res.text): stream_url = match.group("url") if "\\/" in stream_url: # if the URL is json encoded, decode it stream_url = parse_json("\"{}\"".format(stream_url)) if ".mpd" in stream_url: for s in DASHStream.parse_manifest(self.session, stream_url).items(): yield s elif ".mp4" in stream_url: yield match.group(1), HTTPStream(self.session, stream_url) else: log.debug("Non-dash/mp4 stream: {0}".format(stream_url)) match = self._dash_manifest_re.search(res.text) if match: # facebook replaces "<" characters with the substring "\\x3C" manifest = match.group("manifest").replace("\\/", "/") if is_py3: manifest = bytes(unquote_plus(manifest), "utf-8").decode("unicode_escape") else: manifest = unquote_plus(manifest).decode("string_escape") # Ignore unsupported manifests until DASH SegmentBase support is implemented if "SegmentBase" in manifest: log.error("Skipped DASH manifest with SegmentBase streams") else: for s in DASHStream.parse_manifest(self.session, manifest).items(): yield s
def _get_streams(self): streamdata = None if self.get_option("email"): if self.login(self.get_option("email"), self.get_option("password")): log.info("Logged in as {0}".format(self.get_option("email"))) self.save_cookies(lambda c: "steamMachineAuth" in c.name) # Handle steam.tv URLs if self._steamtv_url_re.match(self.url) is not None: # extract the steam ID from the page res = self.session.http.get(self.url) for div in itertags(res.text, 'div'): if div.attributes.get("id") == "webui_config": broadcast_data = html_unescape(div.attributes.get("data-broadcast")) steamid = parse_json(broadcast_data).get("steamid") self.url = self._watch_broadcast_url + steamid # extract the steam ID from the URL steamid = self._url_re.match(self.url).group(1) res = self.session.http.get(self.url) # get the page to set some cookies sessionid = res.cookies.get('sessionid') while streamdata is None or streamdata[u"success"] in ("waiting", "waiting_for_start"): streamdata = self._get_broadcast_stream(steamid, sessionid=sessionid) if streamdata[u"success"] == "ready": return DASHStream.parse_manifest(self.session, streamdata["url"]) elif streamdata[u"success"] == "unavailable": log.error("This stream is currently unavailable") return else: r = streamdata[u"retry"] / 1000.0 log.info("Waiting for stream, will retry again in {} seconds...".format(r)) time.sleep(r)
def _get_streams(self): self.session.http.headers["User-Agent"] = "streamlink/{0}".format(self.session.version) email = self.get_option("email") if email: log.info("Attempting to login to Steam as {0}".format(email)) if self.dologin(email, self.get_option("password")): log.info("Logged in as {0}".format(email)) self.save_cookies(lambda c: "steamMachineAuth" in c.name) if self.matches[1] is None: steamid = self.match.group(1) else: steamid = self._find_steamid(self.url) if not steamid: return self.url = self._watch_broadcast_url.format(steamid=steamid) res = self.session.http.get(self.url) # get the page to set some cookies sessionid = res.cookies.get("sessionid") streamdata = None while streamdata is None or streamdata["success"] in ("waiting", "waiting_for_start"): streamdata = self._get_broadcast_stream(steamid, sessionid=sessionid) if streamdata["success"] == "ready": return DASHStream.parse_manifest(self.session, streamdata["url"]) if streamdata["success"] == "unavailable": log.error("This stream is currently unavailable") return r = streamdata["retry"] / 1000.0 log.info("Waiting for stream, will retry again in {0:.1f} seconds...".format(r)) time.sleep(r)
def mediaselector(self, vpid): urls = defaultdict(set) for platform in self.platforms: url = self.api_url.format(vpid=vpid, vpid_hash=self._hash_vpid(vpid), platform=platform) self.logger.debug("Info API request: {0}", url) medias = http.get(url, schema=self.mediaselector_schema) for media in medias: for connection in media["connection"]: urls[connection.get("transferFormat")].add( connection["href"]) for stream_type, urls in urls.items(): self.logger.debug("{0} {1} streams", len(urls), stream_type) for url in list(urls): self.logger.debug(" {0}", url) if stream_type == "hds": for s in HDSStream.parse_manifest(self.session, url).items(): yield s if stream_type == "hls": for s in HLSStream.parse_variant_playlist( self.session, url).items(): yield s if connection.get("transferFormat") == "dash": for s in DASHStream.parse_manifest( self.session, connection["href"]).items(): yield s
def test_parse_manifest_audio_multi(self, mpdClass): mpdClass.return_value = Mock(periods=[ Mock(adaptationSets=[ Mock(contentProtection=None, representations=[ Mock(id=1, mimeType="video/mp4", height=720), Mock(id=2, mimeType="video/mp4", height=1080), Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang='en'), Mock(id=4, mimeType="audio/aac", bandwidth=256.0, lang='en') ]) ]) ]) streams = DASHStream.parse_manifest(self.session, self.test_url) mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") self.assertSequenceEqual( sorted(list(streams.keys())), sorted(["720p+a128k", "1080p+a128k", "720p+a256k", "1080p+a256k"]))
def test_parse_manifest_with_duplicated_resolutions(self, mpdClass): """ Verify the fix for https://github.com/streamlink/streamlink/issues/3365 """ mpdClass.return_value = Mock(periods=[ Mock(adaptationSets=[ Mock(contentProtection=None, representations=[ Mock(id=1, mimeType="video/mp4", height=1080, bandwidth=128.0), Mock(id=2, mimeType="video/mp4", height=1080, bandwidth=64.0), Mock(id=3, mimeType="video/mp4", height=1080, bandwidth=32.0), Mock(id=4, mimeType="video/mp4", height=720), ]) ]) ]) streams = DASHStream.parse_manifest(self.session, self.test_url) mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") self.assertSequenceEqual( sorted(list(streams.keys())), sorted(["720p", "1080p", "1080p_alt", "1080p_alt2"]))
def _get_streams(self): streamdata = None if self.get_option("email"): if self.login(self.get_option("email"), self.get_option("password")): log.info("Logged in as {0}".format(self.get_option("email"))) self.save_cookies(lambda c: "steamMachineAuth" in c.name) # Handle steam.tv URLs if self.matches[1] is not None: # extract the steam ID from the page res = self.session.http.get(self.url) for div in itertags(res.text, 'div'): if div.attributes.get("id") == "webui_config": broadcast_data = html_unescape(div.attributes.get("data-broadcast")) steamid = parse_json(broadcast_data).get("steamid") self.url = self._watch_broadcast_url + steamid # extract the steam ID from the URL steamid = self.match.group(1) res = self.session.http.get(self.url) # get the page to set some cookies sessionid = res.cookies.get('sessionid') while streamdata is None or streamdata["success"] in ("waiting", "waiting_for_start"): streamdata = self._get_broadcast_stream(steamid, sessionid=sessionid) if streamdata["success"] == "ready": return DASHStream.parse_manifest(self.session, streamdata["url"]) elif streamdata["success"] == "unavailable": log.error("This stream is currently unavailable") return else: r = streamdata["retry"] / 1000.0 log.info("Waiting for stream, will retry again in {} seconds...".format(r)) time.sleep(r)
def _get_streams(self): data = self.match.groupdict() url = update_scheme("https://", data.get("url"), force=False) params = parse_params(data.get("params")) log.debug(f"URL={url}; params={params}") return DASHStream.parse_manifest(self.session, url, **params)
def test_dash_stream_url(session, common_args, expected_headers): # DASHStream requires an MPD instance as input: # The URL of the MPD instance was already prepared by DASHStream.parse_manifest, so copy this behavior here. # This test verifies that session params, headers, etc. are added to the JSON data, without duplicates. args = common_args.copy() args.update(url="http://host/stream.mpd?foo=bar") url = session.http.prepare_new_request(**args).url mpd = Mock(url=url) stream = DASHStream(session, mpd, **common_args) assert stream.__json__() == { "type": "dash", "url": "http://host/stream.mpd?foo=bar&sessionqueryparamkey=sessionqueryparamval&queryparamkey=queryparamval", "headers": expected_headers, }
def test_parse_manifest_audio_multi_lang_locale(self, mpdClass): self.session.localization.language.alpha2 = "es" self.session.localization.explicit = True mpdClass.return_value = Mock(periods=[ Mock(adaptationSets=[ Mock(contentProtection=None, representations=[ Mock(id=1, mimeType="video/mp4", height=720), Mock(id=2, mimeType="video/mp4", height=1080), Mock(id=3, mimeType="audio/aac", bandwidth=128.0, lang='en'), Mock(id=4, mimeType="audio/aac", bandwidth=128.0, lang='es') ]) ]) ]) streams = DASHStream.parse_manifest(self.session, self.test_url) mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") self.assertSequenceEqual(sorted(list(streams.keys())), sorted(["720p", "1080p"])) self.assertEqual(streams["720p"].audio_representation.lang, "es") self.assertEqual(streams["1080p"].audio_representation.lang, "es")
def mediaselector(self, vpid): urls = defaultdict(set) for platform in self.platforms: url = self.api_url.format(vpid=vpid, vpid_hash=self._hash_vpid(vpid), platform=platform) log.debug("Info API request: {0}", url) medias = self.session.http.get(url, schema=self.mediaselector_schema) for media in medias: for connection in media["connection"]: urls[connection.get("transferFormat")].add(connection["href"]) for stream_type, urls in urls.items(): log.debug("{0} {1} streams", len(urls), stream_type) for url in list(urls): try: if stream_type == "hds": for s in HDSStream.parse_manifest(self.session, url).items(): yield s if stream_type == "hls": for s in HLSStream.parse_variant_playlist(self.session, url).items(): yield s if stream_type == "dash": for s in DASHStream.parse_manifest(self.session, url).items(): yield s log.debug(" OK: {0}", url) except: log.debug(" FAIL: {0}", url)
def _get_streams(self): api_urls = self.session.http.get(self.url, schema=self.channel_id_schema) _api_url = list(api_urls)[0] log.debug("API URL: {0}".format(_api_url)) player_api_url = self.session.http.get(_api_url, schema=self.player_api_schema) for api_url in player_api_url: log.debug("Player API URL: {0}".format(api_url)) for source in self.session.http.get(api_url, schema=self.stream_schema): log.debug("Stream source: {0} ({1})".format( source['src'], source.get("type", "n/a"))) if "type" not in source or source[ "type"] == "application/vnd.apple.mpegurl": streams = HLSStream.parse_variant_playlist( self.session, source["src"]) if not streams: yield "live", HLSStream(self.session, source["src"]) else: yield from streams.items() elif source["type"] == "application/dash+xml": yield from DASHStream.parse_manifest( self.session, source["src"]).items()
def mediaselector(self, vpid): urls = defaultdict(set) for platform in self.platforms: url = self.api_url.format(vpid=vpid, vpid_hash=self._hash_vpid(vpid), platform=platform) log.debug(f"Info API request: {url}") medias = self.session.http.get(url, schema=self.mediaselector_schema) for media in medias: for connection in media["connection"]: urls[connection.get("transferFormat")].add( connection["href"]) for stream_type, urls in urls.items(): log.debug(f"{len(urls)} {stream_type} streams") for url in list(urls): try: if stream_type == "hds": yield from HDSStream.parse_manifest(self.session, url).items() if stream_type == "hls": yield from HLSStream.parse_variant_playlist( self.session, url).items() if stream_type == "dash": yield from DASHStream.parse_manifest( self.session, url).items() log.debug(f" OK: {url}") except Exception: log.debug(f" FAIL: {url}")
def test_dash_stream_url(session, common_args): # DASHStream requires an MPD instance as input: # The URL of the MPD instance was already prepared by DASHStream.parse_manifest, so copy this behavior here. # This test verifies that session params are added to the URL, without duplicates. args = common_args.copy() args.update(url="http://host/stream.mpd?foo=bar") url = session.http.prepare_new_request(**args).url mpd = Mock(url=url) stream = DASHStream(session, mpd, **common_args) assert stream.to_url( ) == "http://host/stream.mpd?foo=bar&queryparamkey=queryparamval" with pytest.raises(TypeError) as cm: stream.to_manifest_url() assert str( cm.value ) == "<DASHStream [dash]> cannot be translated to a manifest URL"
def _get_streams(self): m = self.match if m: channel = m.group(1) or m.group(2) log.debug("Found channel {0}".format(channel)) for sformat, url in self.get_stream_urls(channel): try: if sformat == "dash": yield from DASHStream.parse_manifest( self.session, url, headers={ "User-Agent": useragents.CHROME }).items() if sformat == "hls": yield from HLSStream.parse_variant_playlist( self.session, url, headers={ "User-Agent": useragents.IPHONE }, ).items() except PluginError as e: log.error("Could not open {0} stream".format(sformat)) log.debug("Failed with error: {0}".format(e))
def _get_streams(self): streamdata = None if self.get_option("email"): if self.login(self.get_option("email"), self.get_option("password")): log.info("Logged in as {0}".format(self.get_option("email"))) self.save_cookies(lambda c: "steamMachineAuth" in c.name) # extract the steam ID from the URL steamid = self._url_re.match(self.url).group(1) while streamdata is None or streamdata[u"success"] in ( "waiting", "waiting_for_start"): streamdata = self._get_broadcast_stream(steamid) if streamdata[u"success"] == "ready": return DASHStream.parse_manifest(self.session, streamdata["url"]) elif streamdata[u"success"] == "unavailable": log.error("This stream is currently unavailable") return else: r = streamdata[u"retry"] / 1000.0 log.info( "Waiting for stream, will retry again in {} seconds...". format(r)) time.sleep(r)
def _get_video_streams(self): res = self.session.http.get(self.url) match = self._video_player_re.search(res.text) if match is None: return player_url = match.group('player_url') stream_data = self.session.http.get(player_url, schema=self._video_stream_schema) if stream_data is None: return # Check geolocation to prevent further errors when stream is parsed if not self.check_geolocation(stream_data['geoLocRestriction']): log.error('Stream is geo-restricted') return # Check whether streams are DRM-protected if stream_data.get('drm', False): log.error('Stream is DRM-protected') return now = datetime.datetime.now() try: if isinstance(stream_data['sources'], dict): urls = [] for profile, url in stream_data['sources'].items(): if not url or url in urls: continue match = self._stream_size_re.match(url) if match is not None: quality = match.group('size') else: quality = profile yield quality, HTTPStream(self.session, url) urls.append(url) hls_url = stream_data.get('urlHls') or stream_data.get('streamUrlHls') if hls_url: if stream_data.get('isLive', False): # Live streams require a token hls_url = self.tokenize_stream(hls_url) yield from HLSStream.parse_variant_playlist(self.session, hls_url).items() dash_url = stream_data.get('urlDash') or stream_data.get('streamUrlDash') if dash_url: if stream_data.get('isLive', False): # Live streams require a token dash_url = self.tokenize_stream(dash_url) yield from DASHStream.parse_manifest(self.session, dash_url).items() except OSError as err: if '403 Client Error' in str(err): # Check whether video is expired if 'startDate' in stream_data: if now < self.iso8601_to_epoch(stream_data['startDate']): log.error('Stream is not yet available') elif 'endDate' in stream_data: if now > self.iso8601_to_epoch(stream_data['endDate']): log.error('Stream has expired')
def _parse_streams(self, res): _found_stream_url = False for meta in itertags(res.text, "meta"): if meta.attributes.get("property") == "og:video:url": stream_url = html_unescape(meta.attributes.get("content")) if ".mpd" in stream_url: for s in DASHStream.parse_manifest(self.session, stream_url).items(): yield s _found_stream_url = True elif ".mp4" in stream_url: yield "vod", HTTPStream(self.session, stream_url) _found_stream_url = True break else: log.debug("No meta og:video:url") if _found_stream_url: return for match in self._src_re.finditer(res.text): stream_url = match.group("url") if "\\/" in stream_url: # if the URL is json encoded, decode it stream_url = parse_json("\"{}\"".format(stream_url)) if ".mpd" in stream_url: yield from DASHStream.parse_manifest(self.session, stream_url).items() elif ".mp4" in stream_url: yield match.group(1), HTTPStream(self.session, stream_url) else: log.debug("Non-dash/mp4 stream: {0}".format(stream_url)) match = self._dash_manifest_re.search(res.text) if match: # facebook replaces "<" characters with the substring "\\x3C" manifest = match.group("manifest").replace("\\/", "/") manifest = bytes(unquote_plus(manifest), "utf-8").decode("unicode_escape") # Ignore unsupported manifests until DASH SegmentBase support is implemented if "SegmentBase" in manifest: log.error("Skipped DASH manifest with SegmentBase streams") else: yield from DASHStream.parse_manifest(self.session, manifest).items()
def _get_streams(self): if "player.vimeo.com" in self.url: data = self.session.http.get(self.url, schema=self._player_schema) else: api_url = self.session.http.get(self.url, schema=self._config_url_schema) if not api_url: return data = self.session.http.get(api_url, schema=self._config_schema) videos = data["request"]["files"] streams = [] for stream_type in ("hls", "dash"): if stream_type not in videos: continue for _, video_data in videos[stream_type]["cdns"].items(): log.trace("{0!r}".format(video_data)) url = video_data.get("url") if stream_type == "hls": for stream in HLSStream.parse_variant_playlist( self.session, url).items(): streams.append(stream) elif stream_type == "dash": p = urlparse(url) if p.path.endswith("dash.mpd"): # LIVE url = self.session.http.get(url).json()["url"] elif p.path.endswith("master.json"): # VOD url = url.replace("master.json", "master.mpd") else: log.error("Unsupported DASH path: {0}".format(p.path)) continue for stream in DASHStream.parse_manifest(self.session, url).items(): streams.append(stream) for stream in videos.get("progressive", []): streams.append( (stream["quality"], HTTPStream(self.session, stream["url"]))) if self.get_option("mux_subtitles") and data["request"].get( "text_tracks"): substreams = { s["lang"]: HTTPStream(self.session, "https://vimeo.com" + s["url"]) for s in data["request"]["text_tracks"] } for quality, stream in streams: yield quality, MuxedStream(self.session, stream, subtitles=substreams) else: for stream in streams: yield stream
def test_stream_open_video_audio(self, muxer, reader): stream = DASHStream(self.session, Mock(), Mock(id=1, mimeType="video/mp4"), Mock(id=2, mimeType="audio/mp3", lang='en')) open_reader = reader.return_value = Mock() stream.open() self.assertSequenceEqual(reader.mock_calls, [ call(stream, 1, "video/mp4"), call().open(), call(stream, 2, "audio/mp3"), call().open() ]) self.assertSequenceEqual(muxer.mock_calls, [ call(self.session, open_reader, open_reader, copyts=True), call().open() ])
def test_segments_number_time(self, mpdClass): with xml("dash/test_9.mpd") as mpd_xml: mpdClass.return_value = MPD(mpd_xml, base_url="http://test.bar", url="http://test.bar/foo.mpd") streams = DASHStream.parse_manifest(self.session, self.test_url) mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") self.assertSequenceEqual(list(streams.keys()), ['2500k'])
def _get_streams(self): self._get_cookies() self.follow_vk_redirect() video_id = self.match.group("video_id") if not video_id: return log.debug(f"Video ID: {video_id}") try: data = self.session.http.post( self.API_URL, params={"act": "show"}, data={ "act": "show", "al": "1", "video": video_id }, headers={"Referer": self.url}, schema=validate.Schema( validate.transform( lambda text: re.sub(r"^\s*<!--\s*", "", text)), validate.parse_json(), {"payload": list}, validate.get(("payload", -1)), list, validate.get(-1), {"player": { "params": [dict] }}, validate.get(("player", "params", 0)), { validate.optional("hls"): validate.url(), validate.optional("manifest"): validate.startswith("<?xml"), validate.optional("md_author"): validate.any( str, None), validate.optional("md_title"): validate.any(str, None), })) except PluginError: log.error("Could not parse API response") return self.id = video_id self.author = data.get("md_author") self.title = data.get("md_title") hls = data.get("hls") if hls: return HLSStream.parse_variant_playlist(self.session, hls) dash_manifest = data.get("manifest") if dash_manifest: return DASHStream.parse_manifest(self.session, dash_manifest)
def _get_streams(self): root = self.session.http.get(self.url, schema=validate.Schema( validate.parse_html())) for needle, errormsg in ( ( "This service is not available in your Country", "The content is not available in your region", ), ( "Silahkan login Menggunakan akun MyIndihome dan berlangganan minipack", "The content is not available without a subscription", ), ): if validate.Schema( validate.xml_xpath( """.//script[contains(text(), '"{0}"')]""".format( needle))).validate(root): log.error(errormsg) return url = validate.Schema( validate.any( validate.all( validate.xml_xpath_string(""" .//script[contains(text(), 'laylist.m3u8') or contains(text(), 'manifest.mpd')][1]/text() """), validate.text, validate.transform( re.compile( r"""(?P<q>['"])(?P<url>https://.*?/(?:[Pp]laylist\.m3u8|manifest\.mpd).+?)(?P=q)""" ).search), validate.any( None, validate.all(validate.get("url"), validate.url())), ), validate.all( validate.xml_xpath_string( ".//video[@id='video-player']/source/@src"), validate.any(None, validate.url()), ), )).validate(root) if url and ".m3u8" in url: return HLSStream.parse_variant_playlist(self.session, url) elif url and ".mpd" in url: return DASHStream.parse_manifest(self.session, url)
def test_dash_stream(session): mpd = Mock(url=None) stream = DASHStream(session, mpd) with pytest.raises(TypeError) as cm: stream.to_url() assert str(cm.value) == "<DASHStream [dash]> cannot be translated to a URL" with pytest.raises(TypeError) as cm: stream.to_manifest_url() assert str( cm.value ) == "<DASHStream [dash]> cannot be translated to a manifest URL"
def _get_live(self, path): match = self.live_id_re.search(path) if match is None: return live_id = "ch-{0}".format(match.group('live_id')) log.debug("Live ID={0}".format(live_id)) res = self.session.http.get(self.api_url.format(live_id)) api_data = self.session.http.json(res, schema=self._video_schema) self._set_metadata(api_data, 'Live') for playlist in api_data['videoReferences']: if playlist['format'] == 'dashhbbtv': yield from DASHStream.parse_manifest(self.session, playlist['url']).items()
def test_parse_manifest_video_only(self, mpdClass): mpdClass.return_value = Mock(periods=[ Mock(adaptationSets=[ Mock(contentProtection=None, representations=[ Mock(id=1, mimeType="video/mp4", height=720), Mock(id=2, mimeType="video/mp4", height=1080) ]) ]) ]) streams = DASHStream.parse_manifest(self.session, self.test_url) mpdClass.assert_called_with(ANY, base_url="http://test.bar", url="http://test.bar/foo.mpd") self.assertSequenceEqual(sorted(list(streams.keys())), sorted(["720p", "1080p"]))
def _get_streams_api(self, video_id): res = self.session.http.get(self.api_server, params=dict(video_id=video_id)) data = self.session.http.json(res) if data["success"]: for x in itertools.chain(*data['data']['versions'].values()): src = update_scheme("https://", x["src"], force=False) if x['type'] == "application/x-mpegurl": yield from HLSStream.parse_variant_playlist( self.session, src).items() elif x['type'] == "application/dash+xml": yield from DASHStream.parse_manifest(self.session, src).items() elif x['type'] == "video/mp4": yield "{0}p".format(x['res']), HTTPStream( self.session, src) else: log.error("Failed to get streams: {0} ({1})".format( data['message'], data['code']))
def _get_streams(self): stream_id, has_token, hls_url, dash_url = self.session.http.get( self.url, schema=validate.Schema( validate.parse_html(), validate.xml_find(".//*[@data-video-id]"), validate.union(( validate.get("data-video-id"), validate.all( validate.get("data-video-has-token"), validate.transform(lambda val: val and val != "false"), ), validate.get("data-vjs-clip-hls-url"), validate.get("data-vjs-clip-dash-url"), )), ), ) if dash_url and has_token: token = self._get_stream_token(stream_id, "dash") parsed = urlsplit(dash_url) dash_url = urlunsplit( parsed._replace(path=f"{token}{parsed.path}")) return DASHStream.parse_manifest( self.session, dash_url, headers={"Referer": self.url}, ) if not hls_url: return if has_token: token = self._get_stream_token(stream_id, "hls") hls_url = f"{hls_url}?{token}" return HLSStream.parse_variant_playlist( self.session, hls_url, headers={"Referer": self.url}, )
def _get_vod(self): vod_id = self._get_vod_id(self.url) if vod_id is None: res = self.session.http.get(self.url) match = self.latest_episode_url_re.search(res.text) if match is None: return vod_id = self._get_vod_id(match.group("url")) if vod_id is None: return log.debug("VOD ID={0}".format(vod_id)) res = self.session.http.get(self.api_url.format(vod_id)) api_data = self.session.http.json(res, schema=self._video_schema) self._set_metadata(api_data, 'VOD') substreams = {} if 'subtitleReferences' in api_data: for subtitle in api_data['subtitleReferences']: if subtitle['format'] == 'webvtt': log.debug("Subtitle={0}".format(subtitle['url'])) substreams[subtitle['format']] = HTTPStream( self.session, subtitle['url'], ) for manifest in api_data['videoReferences']: if manifest['format'] == 'dashhbbtv': for q, s in DASHStream.parse_manifest(self.session, manifest['url']).items(): if self.get_option('mux_subtitles') and substreams: yield q, MuxedStream(self.session, s, subtitles=substreams) else: yield q, s
def _get_streams(self): page = self.session.http.get(self.url) api_info = self._get_api_info(page) if not api_info: log.error("Could not find API info in page") return token_res = self.session.http.post(api_info["token_url"]) token = self.session.http.json(token_res, schema=self._token_schema) log.debug("Got token: {0}".format(token)) log.debug("Getting stream data: {0}".format(api_info["stream_url"])) res = self.session.http.get(api_info["stream_url"], params={ "vrtPlayerToken": token, "client": "vrtvideo" }, raise_for_status=False) data = self.session.http.json(res, schema=self._stream_schema) if "code" in data: log.error("{0} ({1})".format(data['message'], data['code'])) return log.debug( "Streams have {0}DRM".format("no " if not data["drm"] else "")) for target in data["targetUrls"]: if data["drm"]: if target["type"] == "hls_aes": yield from HLSStream.parse_variant_playlist( self.session, target["url"]).items() elif target["type"] == "hls": yield from HLSStream.parse_variant_playlist( self.session, target["url"]).items() elif target["type"] == "mpeg_dash": yield from DASHStream.parse_manifest(self.session, target["url"]).items()
def _watch(self): log.debug('_watch ...') channel = self.match.group('channel') vod_id = self.match.group('vod_id') recording_id = self.match.group('recording_id') params = {'https_watch_urls': True} if channel: watch_url = f'{self.base_url}/zapi/watch' params_cid = self._get_params_cid(channel) if not params_cid: return params.update(params_cid) elif vod_id: log.debug('Found vod_id: {0}'.format(vod_id)) watch_url = f'{self.base_url}/zapi/avod/videos/{vod_id}/watch' elif recording_id: log.debug('Found recording_id: {0}'.format(recording_id)) watch_url = f'{self.base_url}/zapi/watch/recording/{recording_id}' else: log.debug('Missing watch_url') return zattoo_stream_types = self.get_option('stream-types') for stream_type in zattoo_stream_types: params_stream_type = {'stream_type': stream_type} params.update(params_stream_type) data = self.session.http.post( watch_url, headers=self.headers, data=params, acceptable_status=(200, 402, 403, 404), schema=validate.Schema(validate.parse_json(), validate.any({ 'success': validate.transform(bool), 'stream': { 'watch_urls': [{ 'url': validate.url(), validate.optional('maxrate'): int, validate.optional('audio_channel'): str, }], validate.optional('quality'): str, }, }, { 'success': validate.transform(bool), 'internal_code': int, validate.optional('http_status'): int, })), ) if not data['success']: if data['internal_code'] == 401: log.error(f'invalid stream_type {stream_type}') elif data['internal_code'] == 421: log.error('Unfortunately streaming is not permitted in this country or this channel does not exist.') elif data['internal_code'] == 422: log.error('Paid subscription required for this channel.') log.info('If paid subscription exist, use --zattoo-purge-credentials to start a new session.') else: log.debug(f'unknown error {data!r}') log.debug('Force session reset for watch_url') self.reset_session() continue log.debug(f'Found data for {stream_type}') if stream_type == 'hls7': for url in data['stream']['watch_urls']: yield from HLSStream.parse_variant_playlist(self.session, url['url']).items() elif stream_type == 'dash': for url in data['stream']['watch_urls']: yield from DASHStream.parse_manifest(self.session, url['url']).items()
def _get_streams(self): mpdurl = self._url_re.match(self.url).group(2) self.logger.debug("Parsing MPD URL: {0}".format(mpdurl)) return DASHStream.parse_manifest(self.session, mpdurl)