class ard_live(Plugin): swf_url = "http://live.daserste.de/lib/br-player/swf/main.swf" _url_re = re.compile(r"https?://(www.)?daserste.de/", re.I) _player_re = re.compile(r'''dataURL\s*:\s*(?P<q>['"])(?P<url>.*?)(?P=q)''') _player_url_schema = validate.Schema( validate.transform(_player_re.search), validate.any(None, validate.all(validate.get("url"), validate.text))) _livestream_schema = validate.Schema( validate.xml_findall(".//assets"), validate.filter(lambda x: x.attrib.get("type") != "subtitles"), validate.get(0), validate.xml_findall(".//asset"), [ validate.union({ "url": validate.xml_findtext("./fileName"), "bitrate": validate.xml_findtext("./bitrateVideo") }) ]) @classmethod def can_handle_url(cls, url): return cls._url_re.match(url) is not None def _get_streams(self): data_url = self.session.http.get(self.url, schema=self._player_url_schema) if data_url: res = self.session.http.get(urljoin(self.url, data_url)) stream_info = self.session.http.xml(res, schema=self._livestream_schema) for stream in stream_info: url = stream["url"] try: if ".m3u8" in url: for s in HLSStream.parse_variant_playlist( self.session, url, name_key="bitrate").items(): yield s elif ".f4m" in url: for s in HDSStream.parse_manifest( self.session, url, pvswf=self.swf_url, is_akamai=True).items(): yield s elif ".mp4" in url: yield "{0}k".format(stream["bitrate"]), HTTPStream( self.session, url) except IOError as err: self.logger.warning("Error parsing stream: {0}", err)
class Rtve(Plugin): secret_key = base64.b64decode("eWVMJmRhRDM=") channel_id_re = re.compile(r'<span.*?id="iniIDA">(\d+)</span>') url_re = re.compile(r""" https?://(?:www\.)?rtve\.es/(?:directo|noticias|television|deportes)/.*?/? """, re.VERBOSE) cdn_schema = validate.Schema( validate.transform(parse_xml), validate.xml_findtext(".//url") ) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def __init__(self, url): Plugin.__init__(self, url) self.zclient = ZTNRClient(self.secret_key) http.headers = {"User-Agent": useragents.SAFARI_8} def _get_channel_id(self): res = http.get(self.url) m = self.channel_id_re.search(res.text) return m and int(m.group(1)) def _get_streams(self): channel_id = self._get_channel_id() if channel_id: self.logger.debug("Found channel with id: {0}", channel_id) hls_url = self.zclient.get_cdn_list(channel_id, schema=self.cdn_schema) self.logger.debug("Got stream URL: {0}", hls_url) return HLSStream.parse_variant_playlist(self.session, hls_url) return
def test_failure_schema(self): with pytest.raises(validate.ValidationError) as cm: validate.validate(validate.xml_findtext("."), "not-an-element") assert_validationerror( cm.value, """ ValidationError(Callable): iselement('not-an-element') is not true """)
class Welt(Plugin): _url_vod = "https://www.welt.de/onward/video/play/{0}" _schema = validate.Schema( validate.parse_html(), validate.xml_findtext(".//script[@type='application/json'][@data-content='VideoPlayer.Config']"), validate.parse_json(), validate.get("sources"), validate.filter(lambda obj: obj["extension"] == "m3u8"), validate.get((0, "src")) ) def _get_streams(self): hls_url = self.session.http.get(self.url, schema=self._schema) if "mediathek" in self.url.lower(): url = self._url_vod.format(quote(hls_url, safe="")) hls_url = self.session.http.get(url, headers={"Referer": self.url}).url return HLSStream.parse_variant_playlist(self.session, hls_url, headers={"Referer": self.url})
def _get_streams(self): data_json = self.session.http.get( self.url, schema=validate.Schema( validate.parse_html(), validate.xml_findtext( ".//script[@id='fetchedContextValue'][@type='application/json']" ), validate.any( None, validate.all( validate.parse_json(), {str: dict}, validate.transform(lambda obj: list(obj.items())), validate.filter(lambda item: item[0].startswith( "https://api.ardmediathek.de/page-gateway/pages/") ), validate.any(validate.get((0, 1)), []))))) if not data_json: return schema_data = validate.Schema({ "id": str, "widgets": validate.all( [dict], validate.filter(lambda item: item.get("mediaCollection")), validate.get(0), validate.any( None, validate.all( { "geoblocked": bool, "publicationService": { "name": str, }, "show": validate.any( None, validate.all({"title": str}, validate.get("title"))), "title": str, "mediaCollection": { "embedded": { "_mediaArray": [ validate.all( { "_mediaStreamArray": [ validate.all( { "_quality": validate.any( str, int), "_stream": validate.url(), }, validate.union_get( "_quality", "_stream")) ] }, validate.get("_mediaStreamArray"), validate.transform(dict)) ] } }, }, validate.union_get( "geoblocked", ("mediaCollection", "embedded", "_mediaArray", 0), ("publicationService", "name"), "title", "show", )))) }) data = schema_data.validate(data_json) log.debug(f"Found media id: {data['id']}") if not data["widgets"]: log.info("The content is unavailable") return geoblocked, media, self.author, self.title, show = data["widgets"] if geoblocked: log.info("The content is not available in your region") return if show: self.title = f"{show}: {self.title}" if media.get("auto"): yield from HLSStream.parse_variant_playlist( self.session, media.get("auto")).items() else: for quality, stream in media.items(): yield self._QUALITY_MAP.get(quality, quality), HTTPStream( self.session, stream)
class Neulion(Plugin): """Streamlink Plugin for websites based on Neulion Example urls can be found in tests/test_plugin_neulion.py """ url_re = re.compile( r"""https?:// (?P<domain> www\.(?: ufc\.tv | elevensports\.(?:be|lu|pl|sg|tw) | tennischanneleverywhere\.com ) | watch\.(?: nba\.com | rugbypass\.com ) | fanpass\.co\.nz ) /(?P<vtype>channel|game|video)/.+""", re.VERBOSE) video_info_re = re.compile(r"""program\s*=\s*(\{.*?});""", re.DOTALL) channel_info_re = re.compile(r"""g_channel\s*=\s(\{.*?});""", re.DOTALL) current_video_re = re.compile( r"""(?:currentVideo|video)\s*=\s*(\{[^;]+});""", re.DOTALL) info_fallback_re = re.compile( r""" var\s? (?: currentGameId | programId ) \s?=\s?["']?(?P<id>\d+)["']?; """, re.VERBOSE) stream_api_url = "https://{0}/service/publishpoint" auth_url = "https://{0}/secure/authenticate" auth_schema = validate.Schema(validate.xml_findtext("code")) options = PluginOptions({"username": None, "password": None}) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None @property def _domain(self): match = self.url_re.match(self.url) return match.group("domain") @property def _vtype(self): match = self.url_re.match(self.url) return match.group("vtype") def _get_stream_url(self, video_id, vtype): try: res = http.post(self.stream_api_url.format(self._domain), data={ "id": video_id, "type": vtype, "format": "json" }, headers={"User-Agent": useragents.IPHONE_6}) except Exception as e: if "400 Client Error" in str(e): self.logger.error("Login required") return else: raise e data = http.json(res) return data.get("path") def _get_info(self, text): # try to find video info first m = self.video_info_re.search(text) if not m: m = self.current_video_re.search(text) if not m: # and channel info if that fails m = self.channel_info_re.search(text) if m: js_data = m.group(1) try: return_data = js_to_json(js_data) self.logger.debug("js_to_json") except Exception as e: self.logger.debug("js_to_json_regex_fallback") return_data = js_to_json_regex_fallback(js_data) finally: return return_data def _get_info_fallback(self, text): info_id = self.info_fallback_re.search(text) if info_id: self.logger.debug("Found id from _get_info_fallback") return {"id": info_id.group("id")} def _login(self, username, password): res = http.post(self.auth_url.format(self._domain), data={ "username": username, "password": password, "cookielink": False }) login_status = http.xml(res, schema=self.auth_schema) self.logger.debug("Login status for {0}: {1}", username, login_status) if login_status == "loginlocked": self.logger.error( "The account {0} has been locked, the password needs to be reset" ) return login_status == "loginsuccess" def _get_streams(self): login_username = self.get_option("username") login_password = self.get_option("password") if login_username and login_password: self.logger.debug("Attempting login as {0}", login_username) if self._login(login_username, login_password): self.logger.info("Successfully logged in as {0}", login_username) else: self.logger.info("Failed to login as {0}", login_username) res = http.get(self.url) video = self._get_info(res.text) if not video: video = self._get_info_fallback(res.text) if video: self.logger.debug("Found {type}: {name}".format( type=video.get("type", self._vtype), name=video.get("name", "???"))) surl = self._get_stream_url(video["id"], video.get("type", self._vtype)) if surl: surl = surl.replace("_iphone", "") return HLSStream.parse_variant_playlist(self.session, surl) else: self.logger.error( "Could not get stream URL for video: {name} ({id})".format( id=video.get("id", "???"), name=video.get("name", "???"), )) else: self.logger.error("Could not find any video info on the page")
validate.optional("device_cd"): validate.text, validate.optional("ss1_prm"): validate.text, validate.optional("ss2_prm"): validate.text, validate.optional("ss3_prm"): validate.text } ), "clientlibs": validate.all( validate.transform(_clientlibs_re.search), validate.get(2), validate.text ) }) ) _language_schema = validate.Schema( validate.xml_findtext("./country_code") ) _xml_to_srt_schema = validate.Schema( validate.xml_findall(".//body/div"), [ validate.union([validate.all( validate.getattr("attrib"), validate.get("{http://www.w3.org/XML/1998/namespace}lang") ), validate.all( validate.xml_findall("./p"), validate.transform(lambda x: list(enumerate(x, 1))), [ validate.all( validate.union({
def _get_data(self): root = self.session.http.get(self.url, schema=validate.Schema( validate.parse_html())) try: log.debug("Trying to find source via meta tag") schema = validate.Schema( validate.xml_xpath_string( ".//meta[@property='og:video'][1]/@content"), validate.url()) return schema.validate(root) except PluginError: pass try: log.debug("Trying to find source via next-head") schema = validate.Schema( validate.xml_findtext( ".//script[@type='application/ld+json'][@class='next-head']" ), validate.parse_json(), {"contentUrl": validate.url()}, validate.get("contentUrl")) return schema.validate(root) except PluginError: pass schema_fusion = validate.xml_findtext( ".//script[@type='application/javascript'][@id='fusion-metadata']") schema_video = validate.all({"source": { "hls": validate.url() }}, validate.get(("source", "hls"))) try: log.debug( "Trying to find source via fusion-metadata globalContent") schema = validate.Schema( schema_fusion, validate.transform(self._re_fusion_global_content.search), validate.get("json"), validate.parse_json(), {"result": { "related_content": { "videos": list } }}, validate.get( ("result", "related_content", "videos", 0)), schema_video) return schema.validate(root) except PluginError: pass try: log.debug("Trying to find source via fusion-metadata contentCache") schema = validate.Schema( schema_fusion, validate.transform(self._re_fusion_content_cache.search), validate.get("json"), validate.parse_json(), { "videohub-by-guid-v1": { str: { "data": { "result": { "videos": list } } } } }, validate.get("videohub-by-guid-v1"), validate.transform(lambda obj: obj[list(obj.keys())[0]]), validate.get(("data", "result", "videos", 0)), schema_video) return schema.validate(root) except PluginError: pass
class UFCTV(Plugin): url_re = re.compile(r"https?://(?:www\.)?ufc\.tv/(channel|video)/.+") video_info_re = re.compile(r"""program\s*=\s*(\{.*?});""", re.DOTALL) channel_info_re = re.compile(r"""g_channel\s*=\s(\{.*?});""", re.DOTALL) stream_api_url = "https://www.ufc.tv/service/publishpoint" auth_url = "https://www.ufc.tv/secure/authenticate" auth_schema = validate.Schema(validate.xml_findtext("code")) options = PluginOptions({"username": None, "password": None}) @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def _get_stream_url(self, video_id, vtype="video"): res = http.post(self.stream_api_url, data={ "id": video_id, "type": vtype, "format": "json" }, headers={"User-Agent": useragents.IPHONE_6}) data = http.json(res) return data.get("path") def _get_info(self, url): res = http.get(url) # try to find video info first m = self.video_info_re.search(res.text) if not m: # and channel info if that fails m = self.channel_info_re.search(res.text) return m and js_to_json(m.group(1)) def _login(self, username, password): res = http.post(self.auth_url, data={ "username": username, "password": password, "cookielink": False }) login_status = http.xml(res, schema=self.auth_schema) self.logger.debug("Login status for {0}: {1}", username, login_status) if login_status == "loginlocked": self.logger.error( "The account {0} has been locked, the password needs to be reset" ) return login_status == "loginsuccess" def _get_streams(self): if self.get_option("username") and self.get_option("password"): self.logger.debug("Attempting login as {0}", self.get_option("username")) if self._login(self.get_option("username"), self.get_option("password")): self.logger.info("Successfully logged in as {0}", self.get_option("username")) else: self.logger.info("Failed to login as {0}", self.get_option("username")) video = self._get_info(self.url) if video: self.logger.debug("Found {type}: {name}", **video) surl = self._get_stream_url(video['id'], video.get('type', "video")) surl = surl.replace("_iphone", "") if surl: return HLSStream.parse_variant_playlist(self.session, surl) else: self.logger.error( "Could not get stream URL for video: {name} ({id})", **video) else: self.logger.error("Could not find any video info on the page")
def test_simple(self): element = Element("foo") element.text = "bar" assert validate.validate(validate.xml_findtext("."), element) == "bar"
"""Plugin for NHK World, NHK Japan's english TV channel.""" import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream API_URL = "http://{}.nhk.or.jp/nhkworld/app/tv/hlslive_web.xml" _url_re = re.compile("http(?:s)?://(?:(\w+)\.)?nhk.or.jp/nhkworld") _schema = validate.Schema(validate.xml_findtext("./main_url/wstrm")) class NHKWorld(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) is not None def _get_streams(self): # get the HLS xml from the same sub domain as the main url, defaulting to www sdomain = _url_re.match(self.url).group(1) or "www" res = http.get(API_URL.format(sdomain)) stream_url = http.xml(res, schema=_schema) return HLSStream.parse_variant_playlist(self.session, stream_url) __plugin__ = NHKWorld
class WWENetwork(Plugin): url_re = re.compile(r"https?://network.wwe.com") content_id_re = re.compile(r'''"content_id" : "(\d+)"''') playback_scenario = "HTTP_CLOUD_WIRED" login_url = "https://secure.net.wwe.com/workflow.do" login_page_url = "https://secure.net.wwe.com/enterworkflow.do?flowId=account.login&forwardUrl=http%3A%2F%2Fnetwork.wwe.com" api_url = "https://ws.media.net.wwe.com/ws/media/mf/op-findUserVerifiedEvent/v-2.3" _info_schema = validate.Schema( validate.union({ "status": validate.union({ "code": validate.all(validate.xml_findtext(".//status-code"), validate.transform(int)), "message": validate.xml_findtext(".//status-message"), }), "urls": validate.all(validate.xml_findall(".//url"), [validate.getattr("text")]), validate.optional("fingerprint"): validate.xml_findtext(".//updated-fingerprint"), validate.optional("session_key"): validate.xml_findtext(".//session-key"), "session_attributes": validate.all(validate.xml_findall(".//session-attribute"), [ validate.getattr("attrib"), validate.union({ "name": validate.get("name"), "value": validate.get("value") }) ]) })) arguments = PluginArguments( PluginArgument("email", required=True, metavar="EMAIL", requires=["password"], help=""" The email associated with your WWE Network account, required to access any WWE Network stream. """), PluginArgument("password", sensitive=True, metavar="PASSWORD", help=""" A WWE Network account password to use with --wwenetwork-email. """)) def __init__(self, url): super(WWENetwork, self).__init__(url) http.headers.update({"User-Agent": useragents.CHROME}) self._session_attributes = Cache(filename="plugin-cache.json", key_prefix="wwenetwork:attributes") self._session_key = self.cache.get("session_key") self._authed = self._session_attributes.get( "ipid") and self._session_attributes.get("fprt") @classmethod def can_handle_url(cls, url): return cls.url_re.match(url) is not None def login(self, email, password): self.logger.debug("Attempting login as {0}", email) # sets some required cookies to login http.get(self.login_page_url) # login res = http.post(self.login_url, data=dict(registrationAction='identify', emailAddress=email, password=password, submitButton=""), headers={"Referer": self.login_page_url}, allow_redirects=False) self._authed = "Authentication Error" not in res.text if self._authed: self._session_attributes.set("ipid", res.cookies.get("ipid"), expires=3600 * 1.5) self._session_attributes.set("fprt", res.cookies.get("fprt"), expires=3600 * 1.5) return self._authed def _update_session_attribute(self, key, value): if value: self._session_attributes.set(key, value, expires=3600 * 1.5) # 1h30m expiry http.cookies.set(key, value) @property def session_key(self): return self._session_key @session_key.setter def session_key(self, value): self.cache.set("session_key", value) self._session_key = value def _get_media_info(self, content_id): """ Get the info about the content, based on the ID :param content_id: :return: """ params = { "identityPointId": self._session_attributes.get("ipid"), "fingerprint": self._session_attributes.get("fprt"), "contentId": content_id, "playbackScenario": self.playback_scenario, "platform": "WEB_MEDIAPLAYER_5", "subject": "LIVE_EVENT_COVERAGE", "frameworkURL": "https://ws.media.net.wwe.com", "_": int(time.time()) } if self.session_key: params["sessionKey"] = self.session_key url = self.api_url.format(id=content_id) res = http.get(url, params=params) return http.xml(res, ignore_ns=True, schema=self._info_schema) def _get_content_id(self): # check the page to find the contentId res = http.get(self.url) m = self.content_id_re.search(res.text) if m: return m.group(1) def _get_streams(self): email = self.get_option("email") password = self.get_option("password") if not self._authed and (not email and not password): self.logger.error( "A login for WWE Network is required, use --wwenetwork-email/" "--wwenetwork-password to set them") return if not self._authed: if not self.login(email, password): self.logger.error( "Failed to login, check your username/password") return content_id = self._get_content_id() if content_id: self.logger.debug("Found content ID: {0}", content_id) info = self._get_media_info(content_id) if info["status"]["code"] == 1: # update the session attributes self._update_session_attribute("fprt", info.get("fingerprint")) for attr in info["session_attributes"]: self._update_session_attribute(attr["name"], attr["value"]) if info.get("session_key"): self.session_key = info.get("session_key") for url in info["urls"]: for s in HLSStream.parse_variant_playlist( self.session, url, name_fmt="{pixels}_{bitrate}").items(): yield s else: raise PluginError( "Could not load streams: {message} ({code})".format( **info["status"]))
import re from time import time from streamlink.plugin import Plugin, PluginError from streamlink.plugin.api import http, validate from streamlink.stream import RTMPStream, HLSStream SWF_URL = "http://play.streamingvideoprovider.com/player2.swf" API_URL = "http://player.webvideocore.net/index.php" _url_re = re.compile( "http(s)?://(\w+\.)?streamingvideoprovider.co.uk/(?P<channel>[^/&?]+)") _hls_re = re.compile(r"'(http://.+\.m3u8)'") _rtmp_schema = validate.Schema(validate.xml_findtext("./info/url"), validate.url(scheme="rtmp")) _hls_schema = validate.Schema( validate.transform(_hls_re.search), validate.any( None, validate.all( validate.get(1), validate.url(scheme="http", path=validate.endswith("m3u8"))))) class Streamingvideoprovider(Plugin): @classmethod def can_handle_url(self, url): return _url_re.match(url)
"""Plugin for NHK World, NHK Japan's english TV channel.""" import re from streamlink.plugin import Plugin from streamlink.plugin.api import validate from streamlink.stream import HLSStream API_URL = "http://{}.nhk.or.jp/nhkworld/app/tv/hlslive_web.xml" _url_re = re.compile(r"http(?:s)?://(?:(\w+)\.)?nhk.or.jp/nhkworld") _schema = validate.Schema( validate.xml_findtext("./main_url/wstrm") ) class NHKWorld(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) is not None def _get_streams(self): # get the HLS xml from the same sub domain as the main url, defaulting to www sdomain = _url_re.match(self.url).group(1) or "www" res = self.session.http.get(API_URL.format(sdomain)) stream_url = self.session.http.xml(res, schema=_schema) return HLSStream.parse_variant_playlist(self.session, stream_url) __plugin__ = NHKWorld
def test_xml_findtext(self): el = Element("foo") el.text = "bar" assert validate(xml_findtext("."), el) == "bar"
"HLS", HLSStream.parse_variant_playlist ) } _url_re = re.compile(""" http(s)?://(\w+\.)?zdf.de/zdfmediathek(\#)?/.+ /(live|video) /(?P<video_id>\d+) """, re.VERBOSE | re.IGNORECASE) _schema = validate.Schema( validate.xml_findall("video/formitaeten/formitaet"), [ validate.union({ "type": validate.get("basetype"), "quality": validate.xml_findtext("quality"), "url": validate.all( validate.xml_findtext("url"), validate.url() ) }) ] ) class zdf_mediathek(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) @classmethod
"s": validate.text, "country": validate.text, "init": validate.text, validate.optional("ss_id"): validate.text, validate.optional("mv_id"): validate.text, validate.optional("device_cd"): validate.text, validate.optional("ss1_prm"): validate.text, validate.optional("ss2_prm"): validate.text, validate.optional("ss3_prm"): validate.text }), "clientlibs": validate.all(validate.transform(_clientlibs_re.search), validate.get(2), validate.text) })) _language_schema = validate.Schema(validate.xml_findtext("./country_code")) _xml_to_srt_schema = validate.Schema(validate.xml_findall(".//body/div"), [ validate.union([ validate.all( validate.getattr("attrib"), validate.get("{http://www.w3.org/XML/1998/namespace}lang")), validate.all( validate.xml_findall("./p"), validate.transform(lambda x: list(enumerate(x, 1))), [ validate.all( validate.union({ "i": validate.get(0), "begin": validate.all(
def test_empty(self): element = Element("foo") assert validate.validate(validate.xml_findtext("."), element) is None
from time import time from streamlink.plugin import Plugin, PluginError from streamlink.plugin.api import http, validate from streamlink.stream import RTMPStream, HLSStream SWF_URL = "http://play.streamingvideoprovider.com/player2.swf" API_URL = "http://player.webvideocore.net/index.php" _url_re = re.compile( r"http(s)?://(\w+\.)?streamingvideoprovider.co.uk/(?P<channel>[^/&?]+)" ) _hls_re = re.compile(r"'(http://.+\.m3u8)'") _rtmp_schema = validate.Schema( validate.xml_findtext("./info/url"), validate.url(scheme="rtmp") ) _hls_schema = validate.Schema( validate.transform(_hls_re.search), validate.any( None, validate.all( validate.get(1), validate.url( scheme="http", path=validate.endswith("m3u8") ) ) ) )
from time import time from streamlink.plugin import Plugin, PluginError from streamlink.plugin.api import validate from streamlink.stream import RTMPStream, HLSStream SWF_URL = "http://play.streamingvideoprovider.com/player2.swf" API_URL = "http://player.webvideocore.net/index.php" _url_re = re.compile( r"http(s)?://(\w+\.)?streamingvideoprovider\.co\.uk/(?P<channel>[^/&?]+)" ) _hls_re = re.compile(r"'(http://.+\.m3u8)'") _rtmp_schema = validate.Schema( validate.xml_findtext("./info/url"), validate.url(scheme="rtmp") ) _hls_schema = validate.Schema( validate.transform(_hls_re.search), validate.any( None, validate.all( validate.get(1), validate.url( scheme="http", path=validate.endswith("m3u8") ) ) ) )
"""Plugin for NHK World, NHK Japan's english TV channel.""" import re from streamlink.plugin import Plugin from streamlink.plugin.api import http, validate from streamlink.stream import HLSStream API_URL = "http://{}.nhk.or.jp/nhkworld/app/tv/hlslive_web.xml" _url_re = re.compile(r"http(?:s)?://(?:(\w+)\.)?nhk.or.jp/nhkworld") _schema = validate.Schema( validate.xml_findtext("./main_url/wstrm") ) class NHKWorld(Plugin): @classmethod def can_handle_url(cls, url): return _url_re.match(url) is not None def _get_streams(self): # get the HLS xml from the same sub domain as the main url, defaulting to www sdomain = _url_re.match(self.url).group(1) or "www" res = http.get(API_URL.format(sdomain)) stream_url = http.xml(res, schema=_schema) return HLSStream.parse_variant_playlist(self.session, stream_url) __plugin__ = NHKWorld