def update_scheme(current, target, force=True): # type: (str, str, bool) -> str """ Take the scheme from the current URL and apply it to the target URL if it is missing :param current: current URL :param target: target URL :param force: always apply the current scheme to the target, even if a target scheme exists :return: target URL with the current URL's scheme """ target_p = urlparse(target) if ( # target URLs with implicit scheme and netloc including a port: ("http://", "foo.bar:1234") -> "http://foo.bar:1234" # urllib.parse.urlparse has incorrect behavior in py<3.9, so we'll have to use a regex here # py>=3.9: urlparse("127.0.0.1:1234") == ParseResult(scheme='127.0.0.1', netloc='', path='1234', ...) # py<3.9 : urlparse("127.0.0.1:1234") == ParseResult(scheme='', netloc='', path='127.0.0.1:1234', ...) not _re_uri_implicit_scheme.search(target) and not target.startswith("//") # target URLs without scheme and netloc: ("http://", "foo.bar/foo") -> "http://foo.bar/foo" or not target_p.scheme and not target_p.netloc): return "{0}://{1}".format( urlparse(current).scheme, urlunparse(target_p)) # target URLs without scheme but with netloc: ("http://", "//foo.bar/foo") -> "http://foo.bar/foo" if not target_p.scheme and target_p.netloc: return "{0}:{1}".format(urlparse(current).scheme, urlunparse(target_p)) # target URLs with scheme # override the target scheme if force: return urlunparse(target_p._replace(scheme=urlparse(current).scheme)) # keep the target scheme return target
def update_scheme(current, target): """ Take the scheme from the current URL and applies it to the target URL if the target URL startswith // or is missing a scheme :param current: current URL :param target: target URL :return: target URL with the current URLs scheme """ target_p = urlparse(target) if not target_p.scheme and target_p.netloc: return "{0}:{1}".format(urlparse(current).scheme, urlunparse(target_p)) elif not target_p.scheme and not target_p.netloc: return "{0}://{1}".format(urlparse(current).scheme, urlunparse(target_p)) else: return target
def _get_streams(self): zdf_json = self.session.http.get(self.url, schema=_api_schema) if zdf_json is None: return headers = { "Accept": "application/vnd.de.zdf.v1.0+json", "Api-Auth": "Bearer {0}".format(zdf_json['apiToken']), "Referer": self.url } res = self.session.http.get(zdf_json['content'], headers=headers) document = self.session.http.json(res, schema=_documents_schema) document_url_p = urlparse(zdf_json['content']) api_url = urlunparse( (document_url_p.scheme, document_url_p.netloc, "", "", "", "")) content = document["mainVideoContent"] target = content["http://zdf.de/rels/target"] template = target["http://zdf.de/rels/streams/ptmd-template"] stream_request_url = url_concat( api_url, template.format(playerId="ngplayer_2_3").replace(" ", "")) res = self.session.http.get(stream_request_url, headers=headers) res = self.session.http.json(res, schema=_schema) streams = {} for format_ in self._extract_streams(res): streams.update(format_) return streams
def __init__(self, node, root=None, parent=None, url=None, *args, **kwargs): # top level has no parent super(MPD, self).__init__(node, root=self, *args, **kwargs) # parser attributes self.url = url self.timelines = defaultdict(lambda: -1) self.timelines.update(kwargs.pop("timelines", {})) self.id = self.attr(u"id") self.profiles = self.attr(u"profiles", required=True) self.type = self.attr(u"type", default=u"static", parser=MPDParsers.type) self.minimumUpdatePeriod = self.attr(u"minimumUpdatePeriod", parser=MPDParsers.duration, default=Duration()) self.minBufferTime = self.attr(u"minBufferTime", parser=MPDParsers.duration, required=True) self.timeShiftBufferDepth = self.attr(u"timeShiftBufferDepth", parser=MPDParsers.duration) self.availabilityStartTime = self.attr(u"availabilityStartTime", parser=MPDParsers.datetime, default=datetime.datetime.fromtimestamp(0, utc), # earliest date required=self.type == "dynamic") self.publishTime = self.attr(u"publishTime", parser=MPDParsers.datetime, required=self.type == "dynamic") self.availabilityEndTime = self.attr(u"availabilityEndTime", parser=MPDParsers.datetime) self.mediaPresentationDuration = self.attr(u"mediaPresentationDuration", parser=MPDParsers.duration) self.suggestedPresentationDelay = self.attr(u"suggestedPresentationDelay", parser=MPDParsers.duration) # parse children location = self.children(Location) self.location = location[0] if location else None if self.location: self.url = self.location.text urlp = list(urlparse(self.url)) if urlp[2]: urlp[2], _ = urlp[2].rsplit("/", 1) self._base_url = urlunparse(urlp) self.baseURLs = self.children(BaseURL) self.periods = self.children(Period, minimum=1) self.programInformation = self.children(ProgramInformation)
def _get_vod(self, user_id, video_name): res = self.session.http.get(urljoin(self.api_url, "getVideoByFileName"), params=dict(userId=user_id, videoName=video_name, serverType="web", callback="x")) vod_data = self.session.http.json(res, schema=self.vod_schema) if video_name == vod_data['ShowTitle']: host, base_path = self.server_addr_re.search( vod_data['ServerAddress']).groups() if not host or not base_path: raise PluginError("Could not split 'ServerAddress' components") base_file, file_ext = self.media_file_re.search( vod_data['MediaFile']).groups() if not base_file or not file_ext: raise PluginError("Could not split 'MediaFile' components") media_path = "{0}{1}{2}{3}{4}{5}".format( base_path, vod_data['MediaRoot'], base_file, vod_data['Bitrates'], file_ext, vod_data['StreamingType']) log.debug("Media path={0}".format(media_path)) vod_url = urlunparse((vod_data['ProtocolType'], host, media_path, '', vod_data['Token'], '')) log.debug("URL={0}".format(vod_url)) return HLSStream.parse_variant_playlist(self.session, vod_url)
def __init__(self, node, root=None, parent=None, url=None, *args, **kwargs): # top level has no parent super(MPD, self).__init__(node, root=self, *args, **kwargs) # parser attributes self.url = url self.timelines = defaultdict(lambda: -1) self.timelines.update(kwargs.pop("timelines", {})) self.id = self.attr(u"id") self.profiles = self.attr(u"profiles", required=True) self.type = self.attr(u"type", default=u"static", parser=MPDParsers.type) self.minimumUpdatePeriod = self.attr(u"minimumUpdatePeriod", parser=MPDParsers.duration, default=Duration()) self.minBufferTime = self.attr(u"minBufferTime", parser=MPDParsers.duration, required=True) self.timeShiftBufferDepth = self.attr(u"timeShiftBufferDepth", parser=MPDParsers.duration) self.availabilityStartTime = self.attr(u"availabilityStartTime", parser=MPDParsers.datetime, default=datetime.datetime.fromtimestamp(0, utc), # earliest date required=self.type == "dynamic") self.publishTime = self.attr(u"publishTime", parser=MPDParsers.datetime, required=self.type == "dynamic") self.mediaPresentationDuration = self.attr(u"mediaPresentationDuration", parser=MPDParsers.duration) self.suggestedPresentationDelay = self.attr(u"suggestedPresentationDelay", parser=MPDParsers.duration) # parse children location = self.children(Location) self.location = location[0] if location else None if self.location: self.url = self.location.text urlp = list(urlparse(self.url)) if urlp[2]: urlp[2], _ = urlp[2].rsplit("/", 1) self._base_url = urlunparse(urlp) self.baseURLs = self.children(BaseURL) self.periods = self.children(Period, minimum=1) self.programInformation = self.children(ProgramInformation)
def _get_streams(self): streams = self.session.http.get(self.url, schema=self._stream_schema) if streams is None: return if streams['type'] != 'STATION': return stream_urls = set() for stream in streams['streams']: log.trace('{0!r}'.format(stream)) url = stream['url'] url_no_scheme = urlunparse(urlparse(url)._replace(scheme='')) if url_no_scheme in stream_urls: continue stream_urls.add(url_no_scheme) if stream['contentFormat'] in ('audio/mpeg', 'audio/aac'): yield 'live', HTTPStream(self.session, url, allow_redirects=True) elif stream['contentFormat'] == 'video/MP2T': streams = HLSStream.parse_variant_playlist(self.session, stream["url"]) if not streams: yield stream["quality"], HLSStream(self.session, stream["url"]) else: for s in streams.items(): yield s
def __init__(self, session, baseurl, url, bootstrap, metadata=None, timeout=60, **request_params): Stream.__init__(self, session) self.baseurl = baseurl self.url = url self.bootstrap = bootstrap self.metadata = metadata self.timeout = timeout # Deep copy request params to make it mutable self.request_params = deepcopy(request_params) parsed = urlparse(self.url) if parsed.query: params = parse_qsl(parsed.query) if params: if not self.request_params.get("params"): self.request_params["params"] = {} self.request_params["params"].update(params) self.url = urlunparse( (parsed.scheme, parsed.netloc, parsed.path, None, None, None))
def __init__(self, url): super(YouTube, self).__init__(url) parsed = urlparse(self.url) if parsed.netloc == 'gaming.youtube.com': self.url = urlunparse(parsed._replace(netloc='www.youtube.com')) self.author = None self.title = None self.video_id = None self.session.http.headers.update({'User-Agent': useragents.CHROME})
def auth_url(self, url): parsed = urlparse(url) path, _ = parsed.path.rsplit("/", 1) token_res = http.get(self.token_url, params=dict(acl=path + "/*")) authparams = http.json(token_res, schema=self.token_schema) existing = dict(parse_qsl(parsed.query)) existing.update(dict(parse_qsl(authparams))) return urlunparse(parsed._replace(query=urlencode(existing)))
def get_stream_url(self, event_id): site = self.match.group(1) or self.match.group(2) api_url = self.api_url.format(id=event_id, site=site.upper()) log.debug("Calling API: {0}".format(api_url)) stream_url = self.session.http.get(api_url).text.strip("\"'") parsed = urlparse(stream_url) query = dict(parse_qsl(parsed.query)) return urlunparse(parsed._replace(query="")), query
def auth_url(self, url): parsed = urlparse(url) path, _ = parsed.path.rsplit("/", 1) token_res = self.session.http.get(self.token_url, params=dict(acl=path + "/*")) authparams = self.session.http.json(token_res, schema=self.token_schema) existing = dict(parse_qsl(parsed.query)) existing.update(dict(parse_qsl(authparams))) return urlunparse(parsed._replace(query=urlencode(existing)))
def handle_module_info(self, args): res = {} for arg in args: if "cdnConfig" in arg: parts = [ # scheme arg["cdnConfig"]["protocol"], # netloc arg["cdnConfig"]["data"][0]["data"][0]["sites"][0]["host"], # path arg["cdnConfig"]["data"][0]["data"][0]["sites"][0]["path"], "", "", "", # params, query, fragment ] # Example: # LIVE: http://uhs-akamai.ustream.tv/ # VOD: http://vod-cdn.ustream.tv/ res["cdn_url"] = urlunparse(parts) if "stream" in arg and bool(arg["stream"].get("streamFormats")): data = arg["stream"] if data["streamFormats"].get("flv/segmented"): flv_segmented = data["streamFormats"]["flv/segmented"] path = flv_segmented["contentAccess"]["accessList"][0][ "data"]["path"] res["streams"] = [] for stream in flv_segmented["streams"]: res["streams"] += [ dict( stream_name="{0}p".format( stream["videoCodec"]["height"]), path=urljoin( path, stream["segmentUrl"].replace("%", "%s")), hashes=flv_segmented["hashes"], first_chunk=flv_segmented["chunkId"], chunk_time=flv_segmented["chunkTime"], ) ] elif bool(data["streamFormats"]): # supported formats: # - flv/segmented # unsupported formats: # - flv # - mp4 # - mp4/segmented raise PluginError( "Stream format is not supported: {0}".format(", ".join( data["streamFormats"].keys()))) elif "stream" in arg and arg["stream"]["contentAvailable"] is False: log.error("This stream is currently offline") raise ModuleInfoNoStreams return res
def get_stream_url(self, event_id): url_m = self.url_re.match(self.url) site = url_m.group(1) or url_m.group(2) api_url = self.api_url.format(id=event_id, site=site.upper()) self.logger.debug("Calling API: {0}", api_url) stream_url = http.get(api_url).text.strip("\"'") parsed = urlparse(stream_url) query = dict(parse_qsl(parsed.query)) return urlunparse(parsed._replace(query="")), query
def _get_streams(self): url_match = self.url_re.match(self.url) url_type, show_name, episode_name = url_match.groups() if url_type == 'streams' and not show_name: url_type = 'live-stream' elif not show_name: raise PluginError("Missing show_name for url_type: {0}".format( url_type, )) log.debug("URL type={0}".format(url_type)) if url_type == 'live-stream': video_id = self._get_stream_data(url_type) elif url_type == 'streams': video_id = self._get_stream_data(show_name) elif url_type == 'videos': if show_name is None or episode_name is None: raise PluginError( "Missing show_name or episode_name for url_type: {0}". format(url_type, )) video_id = self._get_video_data(episode_name) else: raise PluginError("Unrecognised url_type: {0}".format(url_type)) if video_id is None: raise PluginError("Could not find video_id") log.debug("Video ID={0}".format(video_id)) res = self.session.http.get(self.video_data_url.format(video_id)) url_data = self.session.http.json(res, schema=self._api_schema) if 'unprotected' in url_data: url = url_data['unprotected']['url'] elif 'bulkaes' in url_data: url_parsed = urlparse(url_data['bulkaes']['url']) token = self._get_token(url_parsed.path) url = urlunparse(( url_parsed.scheme, url_parsed.netloc, url_parsed.path, url_parsed.params, "{0}={1}".format('hdnts', token), url_parsed.fragment, )) else: raise PluginError("Could not find a usable URL in url_data") log.debug("URL={0}".format(url)) return HLSStream.parse_variant_playlist(self.session, url)
def __init__(self, url): super(YouTube, self).__init__(url) parsed = urlparse(url) # translate input URLs to be able to find embedded data and to avoid unnecessary HTTP redirects if parsed.netloc == "gaming.youtube.com": self.url = urlunparse( parsed._replace(scheme="https", netloc="www.youtube.com")) elif self.match.group("video_id_short") is not None: self.url = self._url_canonical.format( video_id=self.match.group("video_id_short")) elif self.match.group("embed") is not None: self.url = self._url_canonical.format( video_id=self.match.group("video_id")) elif self.match.group("embed_live") is not None: self.url = self._url_channelid_live.format( channel_id=self.match.group("embed_live")) elif parsed.scheme != "https": self.url = urlunparse(parsed._replace(scheme="https")) self.session.http.headers.update({'User-Agent': useragents.CHROME}) consent = self.cache.get("consent_ck") if consent is not None: self.set_consent_ck(consent)
def parse_manifest(cls, session, url, **args): """ Attempt to parse a DASH manifest file and return its streams :param session: Streamlink session instance :param url: URL of the manifest file :return: a dict of name -> DASHStream instances """ ret = {} res = session.http.get(url, **args) url = res.url urlp = list(urlparse(url)) urlp[2], _ = urlp[2].rsplit("/", 1) mpd = MPD(session.http.xml(res, ignore_ns=True), base_url=urlunparse(urlp), url=url) video, audio = [], [] # Search for suitable video and audio representations for aset in mpd.periods[0].adaptationSets: if aset.contentProtection: raise PluginError("{} is protected by DRM".format(url)) for rep in aset.representations: if rep.mimeType.startswith("video"): video.append(rep) elif rep.mimeType.startswith("audio"): audio.append(rep) if not video: video = [None] if not audio: audio = [None] for vid, aud in itertools.product(video, audio): stream = DASHStream(session, mpd, vid, aud, **args) stream_name = [] if vid: stream_name.append("{:0.0f}{}".format( vid.height or vid.bandwidth, "p" if vid.height else "k")) if audio and len(audio) > 1: stream_name.append("a{:0.0f}k".format(aud.bandwidth)) ret['+'.join(stream_name)] = stream return ret
def handle_module_info(self, args): res = {} for arg in args: if "cdnConfig" in arg: parts = [ # scheme arg["cdnConfig"]["protocol"], # netloc arg["cdnConfig"]["data"][0]["data"][0]["sites"][0]["host"], # path arg["cdnConfig"]["data"][0]["data"][0]["sites"][0]["path"], "", "", "", # params, query, fragment ] # Example: # LIVE: http://uhs-akamai.ustream.tv/ # VOD: http://vod-cdn.ustream.tv/ res["cdn_url"] = urlunparse(parts) if "stream" in arg and bool(arg["stream"].get("streamFormats")): data = arg["stream"] if data["streamFormats"].get("flv/segmented"): flv_segmented = data["streamFormats"]["flv/segmented"] path = flv_segmented["contentAccess"]["accessList"][0]["data"]["path"] res["streams"] = [] for stream in flv_segmented["streams"]: res["streams"] += [dict( stream_name=stream["preset"], path=urljoin(path, stream["segmentUrl"].replace("%", "%s")), hashes=flv_segmented["hashes"], first_chunk=flv_segmented["chunkId"], chunk_time=flv_segmented["chunkTime"], )] elif bool(data["streamFormats"]): # supported formats: # - flv/segmented # unsupported formats: # - flv # - mp4 # - mp4/segmented raise PluginError("Stream format is not supported: {0}".format( ", ".join(data["streamFormats"].keys()))) elif "stream" in arg and arg["stream"]["contentAvailable"] is False: log.error("This stream is currently offline") raise ModuleInfoNoStreams return res
def _get_streams(self): json_url = self.session.http.get(self.url, schema=self._schema_data) if not json_url: return json_url = urlunparse(urlparse(self.url)._replace(path=json_url)) log.debug("Found JSON URL: {0}".format(json_url)) stream_url = self.session.http.get(json_url, schema=self._schema_json) log.debug("Found stream URL: {0}".format(stream_url)) res = self.session.http.request("HEAD", stream_url) # status code will be 200 even if geo-blocked, so check the returned content-type if not res or not res.headers or res.headers["Content-Type"] == "video/mp4": log.error("Geo-restricted content") return for stream in HLSStream.parse_variant_playlist(self.session, stream_url).items(): yield stream
def parse_manifest(cls, session, url, **args): """ Attempt to parse a DASH manifest file and return its streams :param session: Streamlink session instance :param url: URL of the manifest file :return: a dict of name -> DASHStream instances """ ret = {} res = session.http.get(url, **args) url = res.url urlp = list(urlparse(url)) urlp[2], _ = urlp[2].rsplit("/", 1) mpd = MPD(session.http.xml(res, ignore_ns=True), base_url=urlunparse(urlp), url=url) video, audio = [], [] # Search for suitable video and audio representations for aset in mpd.periods[0].adaptationSets: if aset.contentProtection: raise PluginError("{} is protected by DRM".format(url)) for rep in aset.representations: if rep.mimeType.startswith("video"): video.append(rep) elif rep.mimeType.startswith("audio"): audio.append(rep) if not video: video = [None] if not audio: audio = [None] locale = session.localization locale_lang = locale.language lang = None available_languages = set() # if the locale is explicitly set, prefer that language over others for aud in audio: if aud and aud.lang: available_languages.add(aud.lang) try: if locale.explicit and aud.lang and Language.get( aud.lang) == locale_lang: lang = aud.lang except LookupError: continue if not lang: # filter by the first language that appears lang = audio[0] and audio[0].lang log.debug( "Available languages for DASH audio streams: {0} (using: {1})". format(", ".join(available_languages) or "NONE", lang or "n/a")) # if the language is given by the stream, filter out other languages that do not match if len(available_languages) > 1: audio = list( filter(lambda a: a.lang is None or a.lang == lang, audio)) for vid, aud in itertools.product(video, audio): stream = DASHStream(session, mpd, vid, aud, **args) stream_name = [] if vid: stream_name.append("{:0.0f}{}".format( vid.height or vid.bandwidth_rounded, "p" if vid.height else "k")) if audio and len(audio) > 1: stream_name.append("a{:0.0f}k".format(aud.bandwidth)) ret['+'.join(stream_name)] = stream return ret
def _handle_module_info_cdn_config(self, data): # type: (Dict) self.stream_cdn = urlunparse( (data["protocol"], data["data"][0]["data"][0]["sites"][0]["host"], data["data"][0]["data"][0]["sites"][0]["path"], "", "", "")) self._set_ready()
def parse_manifest(cls, session, url_or_manifest, **args): """ Attempt to parse a DASH manifest file and return its streams :param session: Streamlink session instance :param url_or_manifest: URL of the manifest file or an XML manifest string :return: a dict of name -> DASHStream instances """ if url_or_manifest.startswith('<?xml'): mpd = MPD(parse_xml(url_or_manifest, ignore_ns=True)) else: res = session.http.get(url_or_manifest, **session.http.valid_request_args(**args)) url = res.url urlp = list(urlparse(url)) urlp[2], _ = urlp[2].rsplit("/", 1) mpd = MPD(session.http.xml(res, ignore_ns=True), base_url=urlunparse(urlp), url=url) video, audio = [], [] # Search for suitable video and audio representations for aset in mpd.periods[0].adaptationSets: if aset.contentProtection: raise PluginError("{} is protected by DRM".format(url)) for rep in aset.representations: if rep.mimeType.startswith("video"): video.append(rep) elif rep.mimeType.startswith("audio"): audio.append(rep) if not video: video = [None] if not audio: audio = [None] locale = session.localization locale_lang = locale.language lang = None available_languages = set() # if the locale is explicitly set, prefer that language over others for aud in audio: if aud and aud.lang: available_languages.add(aud.lang) try: if locale.explicit and aud.lang and Language.get(aud.lang) == locale_lang: lang = aud.lang except LookupError: continue if not lang: # filter by the first language that appears lang = audio[0] and audio[0].lang log.debug("Available languages for DASH audio streams: {0} (using: {1})".format( ", ".join(available_languages) or "NONE", lang or "n/a" )) # if the language is given by the stream, filter out other languages that do not match if len(available_languages) > 1: audio = list(filter(lambda a: a.lang is None or a.lang == lang, audio)) ret = [] for vid, aud in itertools.product(video, audio): stream = DASHStream(session, mpd, vid, aud, **args) stream_name = [] if vid: stream_name.append("{:0.0f}{}".format(vid.height or vid.bandwidth_rounded, "p" if vid.height else "k")) if audio and len(audio) > 1: stream_name.append("a{:0.0f}k".format(aud.bandwidth)) ret.append(('+'.join(stream_name), stream)) # rename duplicate streams dict_value_list = defaultdict(list) for k, v in ret: dict_value_list[k].append(v) ret_new = {} for q in dict_value_list: items = dict_value_list[q] try: items = sorted(items, key=lambda k: k.video_representation.bandwidth, reverse=True) except AttributeError: pass for n in range(len(items)): if n == 0: ret_new[q] = items[n] elif n == 1: ret_new['{0}_alt'.format(q)] = items[n] else: ret_new['{0}_alt{1}'.format(q, n)] = items[n] return ret_new
def __init__(self, url): super(BBCiPlayer, self).__init__(url) self.url = urlunparse(urlparse(self.url)._replace(scheme="https"))
def _get_streams(self): match = url_re.match(self.url) stream_page_scheme = 'https' stream_page_domain = match.group(4) stream_page_path = match.group(5) country_code = CONST_DEFAULT_COUNTRY_CODE is_paid_show = False # create http session and set headers http_session = http http_session.headers.update(CONST_HEADERS) # get swf url and cookies r = http_session.get( urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) # redirect to profile page means stream is offline if '/profile/' in r.url: raise NoStreamsError(self.url) if not r.ok: self.logger.debug("Status code for {}: {}", r.url, r.status_code) raise NoStreamsError(self.url) if len(http_session.cookies) == 0: raise PluginError("Can't get a cookies") if urlparse(r.url).netloc != stream_page_domain: # then redirected to regional subdomain country_code = urlparse(r.url).netloc.split('.')[0].lower() # time to set variables baseurl = urlunparse( (stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) stream_page_url = urljoin(baseurl, stream_page_path) match = swf_re.search(r.text) if match: swf_url = urljoin(baseurl, match.group()) self.logger.debug("swf url found: {}", swf_url) else: # most likely it means that country/region banned # can try use default swf-url swf_url = urljoin(baseurl, CONST_DEFAULT_SWF_LOCATION) self.logger.debug("swf url not found. Will try {}", swf_url) # create amf query amf_message = AMFMessage("svDirectAmf.getRoomData", "/1", [stream_page_path, is_paid_show]) amf_packet = AMFPacket(version=0) amf_packet.messages.append(amf_message) # send request and close http-session r = http_session.post(url=amf_gateway_url, params={CONST_AMF_GATEWAY_PARAM: country_code}, data=bytes(amf_packet.serialize())) http_session.close() if r.status_code != 200: raise PluginError("unexpected status code for {}: {}", r.url, r.status_code) amf_response = AMFPacket.deserialize(BytesIO(r.content)) if len(amf_response.messages ) != 1 or amf_response.messages[0].target_uri != "/1/onResult": raise PluginError("unexpected response from amf gate") stream_source_info = amf_msg_schema.validate( amf_response.messages[0].value) self.logger.debug("source stream info:\n{}", stream_source_info) stream_params = { "live": True, "realtime": True, "flashVer": CONST_FLASH_VER, "swfUrl": swf_url, "tcUrl": stream_source_info['localData']['NC_ConnUrl'], "rtmp": stream_source_info['localData']['NC_ConnUrl'], "pageUrl": stream_page_url, "playpath": "%s?uid=%s" % (''.join(('stream_', stream_page_path)), self._get_stream_uid(stream_source_info['userData']['username'])), "conn": [ "S:{0}".format(stream_source_info['userData']['username']), "S:{0}".format( stream_source_info['localData']['NC_AccessKey']), "B:0", "S:{0}".format(stream_source_info['localData']['dataKey']) ] } self.logger.debug("Stream params:\n{}", stream_params) stream = RTMPStream(self.session, stream_params) return {'live': stream}
def _get_streams(self): match = url_re.match(self.url) stream_page_scheme = 'https' stream_page_domain = match.group(4) stream_page_path = match.group(5) country_code = CONST_DEFAULT_COUNTRY_CODE # create http session and set headers http_session = http http_session.headers.update(CONST_HEADERS) # get cookies r = http_session.get( urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) # redirect to profile page means stream is offline if '/profile/' in r.url: print(colored('\n => Performer is OFFLINE <=', 'yellow', 'on_red')) print(colored('\n => END <= ', 'yellow', 'on_blue')) time.sleep(6) sys.exit() if not r.ok: self.logger.debug('Status code for {0}: {1}', r.url, r.status_code) raise NoStreamsError(self.url) if len(http_session.cookies) == 0: raise PluginError("Can't get a cookies") if urlparse(r.url).netloc != stream_page_domain: # then redirected to regional subdomain country_code = urlparse(r.url).netloc.split('.')[0].lower() # time to set variables baseurl = urlunparse( (stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) stream_page_url = urljoin(baseurl, stream_page_path) headers = { 'User-Agent': useragents.CHROME, 'Referer': stream_page_url, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' } data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format( stream_page_path) self.logger.debug('DATA: {0}'.format(str(data))) # send request and close http-session r = http_session.post(url=amf_gateway_url, headers=headers, params={CONST_AMF_GATEWAY_PARAM: country_code}, data=data) http_session.close() if r.status_code != 200: raise PluginError('unexpected status code for {0}: {1}', r.url, r.status_code) stream_source_info = amf_msg_schema.validate(json.loads(r.text)) self.logger.debug('source stream info:\n{0}', stream_source_info) if not stream_source_info: return performer = stream_source_info['performerData']['username'] real_name = stream_source_info['performerData']['displayName'] performer_id = stream_source_info['performerData']['userId'] print(colored('\n => Performer => {} <=', 'yellow', 'on_blue')).format(real_name) print(colored('\n => Performer ID => {} <=', 'yellow', 'on_blue')).format(performer_id) urlnoproto = stream_source_info['localData']['videoServerUrl'] urlnoproto = update_scheme('https://', urlnoproto) hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format( urlnoproto, performer) server = re.sub('https://', '', urlnoproto) print(colored('\n => Server => {} <=', 'yellow', 'on_blue')).format(server) if hls_url: try: for s in HLSStream.parse_variant_playlist( self.session, hls_url, headers=headers).items(): timestamp = str(time.strftime('%d%m%Y-%H%M%S')) path = config.get('folders', 'output_folder_BC') fn = real_name + '_BC_' + timestamp + '.flv' pf = (path + fn) ffmpeg = config.get('files', 'ffmpeg') print( colored('\n => FFMPEG-24/7-REC => {} <=', 'yellow', 'on_red')).format(fn) print command = ( '{} -hide_banner -loglevel panic -i {} -c:v copy -c:a aac -b:a 160k {}' .format(ffmpeg, hls_url, pf)) os.system(command) print(colored(' => END <= ', 'yellow', 'on_blue')) sys.exit() except Exception as e: if '404' in str(e): print( colored('\n => Performer is AWAY or PRIVATE <=', 'yellow', 'on_red')) print(colored('\n => END <= ', 'yellow', 'on_blue')) time.sleep(6) sys.exit()
def _get_streams(self): match = url_re.match(self.url) stream_page_scheme = 'https' stream_page_domain = match.group(4) stream_page_path = match.group(5) country_code = CONST_DEFAULT_COUNTRY_CODE # create http session and set headers http_session = http http_session.headers.update(CONST_HEADERS) # get cookies r = http_session.get( urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) # redirect to profile page means stream is offline if '/profile/' in r.url: print(colored('\n => Performer is OFFLINE <=', 'yellow', 'on_red')) print(colored('\n => END <= ', 'yellow', 'on_blue')) time.sleep(6) sys.exit() if not r.ok: self.logger.debug('Status code for {0}: {1}', r.url, r.status_code) raise NoStreamsError(self.url) if len(http_session.cookies) == 0: raise PluginError("Can't get a cookies") if urlparse(r.url).netloc != stream_page_domain: # then redirected to regional subdomain country_code = urlparse(r.url).netloc.split('.')[0].lower() # time to set variables baseurl = urlunparse( (stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) stream_page_url = urljoin(baseurl, stream_page_path) headers = { 'User-Agent': useragents.CHROME, 'Referer': stream_page_url, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' } data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format( stream_page_path) self.logger.debug('DATA: {0}'.format(str(data))) # send request and close http-session r = http_session.post(url=amf_gateway_url, headers=headers, params={CONST_AMF_GATEWAY_PARAM: country_code}, data=data) http_session.close() if r.status_code != 200: raise PluginError('unexpected status code for {0}: {1}', r.url, r.status_code) stream_source_info = amf_msg_schema.validate(json.loads(r.text)) self.logger.debug('source stream info:\n{0}', stream_source_info) if not stream_source_info: return performer = stream_source_info['performerData']['username'] real_name = stream_source_info['performerData']['displayName'] performer_id = stream_source_info['performerData']['userId'] print(colored('\n => Performer => {} <=', 'yellow', 'on_blue')).format(real_name) print(colored('\n => Performer ID => {} <=', 'yellow', 'on_blue')).format(performer_id) urlnoproto = stream_source_info['localData']['videoServerUrl'] urlnoproto = update_scheme('https://', urlnoproto) hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format( urlnoproto, performer) server = re.sub('https://', '', urlnoproto) print(colored('\n => Server => {} <=', 'yellow', 'on_blue')).format(server) if hls_url: try: for s in HLSStream.parse_variant_playlist( self.session, hls_url, headers=headers).items(): while True: try: print mode = int( raw_input( colored( ' => Mode => EXIT(5) YTDL-TS(4) SL(3) LS(2) FFMPEG(1) FFPLAY(0) => ', 'yellow', 'on_blue'))) break except ValueError: print( colored('\n => Input must be a number <=', 'yellow', 'on_red')) if mode == 0: mod = 'FFPLAY' if mode == 1: mod = 'FFMPEG' if mode == 2: mod = 'LS' if mode == 3: mod = 'SL' if mode == 4: mod = 'YTDL-TS' if mode == 5: mod = 'EXIT' timestamp = str(time.strftime('%d%m%Y-%H%M%S')) stime = str(time.strftime('%H:%M:%S')) path = config.get('folders', 'output_folder_BC') fn = real_name + '_BC_' + timestamp fn1 = real_name + '_BC_' + timestamp + '.flv' fn2 = real_name + '_BC_' + timestamp + '.mp4' fn3 = real_name + '_BC_' + timestamp + '.ts' pf1 = (path + fn1) pf2 = (path + fn2) pf3 = (path + fn3) ffmpeg = config.get('files', 'ffmpeg') ffplay = config.get('files', 'ffplay') livestreamer = config.get('files', 'livestreamer') streamlink = config.get('files', 'streamlink') youtube = config.get('files', 'youtube') if mod == 'FFPLAY': print( colored('\n => HLS URL => {} <=', 'yellow', 'on_blue')).format(hls_url) print( colored('\n => FFPLAY => {} <=', 'yellow', 'on_magenta')).format(fn) print command = ( '{} -hide_banner -loglevel panic -i {} -infbuf -autoexit -window_title "{} * {} * {}"' .format(ffplay, hls_url, real_name, stime, urlnoproto)) os.system(command) print(colored(' => END <= ', 'yellow', 'on_blue')) if mod == 'FFMPEG': print( colored('\n => HLS URL => {} <=', 'yellow', 'on_blue')).format(hls_url) print( colored('\n => FFMPEG-REC => {} <=', 'yellow', 'on_red')).format(fn1) print command = ( '{} -hide_banner -loglevel panic -i {} -c:v copy -c:a aac -b:a 160k {}' .format(ffmpeg, hls_url, pf1)) os.system(command) print(colored(' => END <= ', 'yellow', 'on_blue')) if mod == 'LS': print( colored('\n => LS-REC >>> {} <<<', 'yellow', 'on_red')).format(fn2) print command = ( '{} hlsvariant://"{}" best -Q -o "{}"'.format( livestreamer, hls_url, pf2)) os.system(command) print(colored(' => END <= ', 'yellow', 'on_blue')) if mod == 'SL': print( colored('\n => SL-REC >>> {} <<<', 'yellow', 'on_red')).format(fn2) print command = ('{} hls://"{}" best -Q -o "{}"'.format( streamlink, hls_url, pf2)) os.system(command) print(colored(' => END <= ', 'yellow', 'on_blue')) if mod == 'YTDL-TS': print( colored('\n => HLS URL => {} <=', 'yellow', 'on_blue')).format(hls_url) print( colored('\n => YTDL-TS-REC => {} <=', 'yellow', 'on_red')).format(fn3) command = ( '{} --hls-use-mpegts --no-part {} -q -o {}'.format( youtube, hls_url, pf3)) os.system(command) print(colored('\n => END <= ', 'yellow', 'on_blue')) sys.exit() if mod == 'EXIT': print(colored('\n => END <= ', 'yellow', 'on_blue')) time.sleep(3) sys.exit() except Exception as e: if '404' in str(e): print( colored('\n => Performer is AWAY or PRIVATE <=', 'yellow', 'on_red')) print(colored('\n => END <= ', 'yellow', 'on_blue')) time.sleep(6) sys.exit()
def _get_streams(self): match = url_re.match(self.url) stream_page_scheme = 'https' stream_page_domain = match.group(4) stream_page_path = match.group(5) country_code = CONST_DEFAULT_COUNTRY_CODE # create http session and set headers http_session = http http_session.headers.update(CONST_HEADERS) # get cookies r = http_session.get( urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) # redirect to profile page means stream is offline if '/profile/' in r.url: raise NoStreamsError(self.url) if not r.ok: self.logger.debug("Status code for {0}: {1}", r.url, r.status_code) raise NoStreamsError(self.url) if len(http_session.cookies) == 0: raise PluginError("Can't get a cookies") if urlparse(r.url).netloc != stream_page_domain: # then redirected to regional subdomain country_code = urlparse(r.url).netloc.split('.')[0].lower() # time to set variables baseurl = urlunparse( (stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) stream_page_url = urljoin(baseurl, stream_page_path) headers = { 'User-Agent': useragents.CHROME, 'Referer': stream_page_url, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' } data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format( stream_page_path) self.logger.debug('DATA: {0}'.format(str(data))) # send request and close http-session r = http_session.post(url=amf_gateway_url, headers=headers, params={CONST_AMF_GATEWAY_PARAM: country_code}, data=data) http_session.close() if r.status_code != 200: raise PluginError("unexpected status code for {0}: {1}", r.url, r.status_code) stream_source_info = amf_msg_schema.validate(json.loads(r.text)) self.logger.debug("source stream info:\n{0}", stream_source_info) if not stream_source_info: return urlnoproto = stream_source_info['localData']['videoServerUrl'] urlnoproto = update_scheme('https://', urlnoproto) performer = stream_source_info['performerData']['username'] hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format( urlnoproto, performer) if hls_url: self.logger.debug('HLS URL: {0}'.format(hls_url)) try: for s in HLSStream.parse_variant_playlist( self.session, hls_url, headers=headers).items(): yield s except Exception as e: if '404' in str(e): self.logger.error('Stream is currently offline or private') else: self.logger.error(str(e)) return
def _get_streams(self): match = url_re.match(self.url) stream_page_scheme = 'https' stream_page_domain = match.group(4) stream_page_path = match.group(5) country_code = CONST_DEFAULT_COUNTRY_CODE # create http session and set headers http_session = http http_session.headers.update(CONST_HEADERS) # get cookies r = http_session.get(urlunparse((stream_page_scheme, stream_page_domain, stream_page_path, '', '', ''))) # redirect to profile page means stream is offline if '/profile/' in r.url: raise NoStreamsError(self.url) if not r.ok: self.logger.debug("Status code for {0}: {1}", r.url, r.status_code) raise NoStreamsError(self.url) if len(http_session.cookies) == 0: raise PluginError("Can't get a cookies") if urlparse(r.url).netloc != stream_page_domain: # then redirected to regional subdomain country_code = urlparse(r.url).netloc.split('.')[0].lower() # time to set variables baseurl = urlunparse((stream_page_scheme, urlparse(r.url).netloc, '', '', '', '')) amf_gateway_url = urljoin(baseurl, CONST_AMF_GATEWAY_LOCATION) stream_page_url = urljoin(baseurl, stream_page_path) headers = { 'User-Agent': useragents.CHROME, 'Referer': stream_page_url, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' } data = 'method=getRoomData&args%5B%5D={0}&args%5B%5D=false'.format(stream_page_path) self.logger.debug('DATA: {0}'.format(str(data))) # send request and close http-session r = http_session.post(url=amf_gateway_url, headers=headers, params={CONST_AMF_GATEWAY_PARAM: country_code}, data=data) http_session.close() if r.status_code != 200: raise PluginError("unexpected status code for {0}: {1}", r.url, r.status_code) stream_source_info = amf_msg_schema.validate(json.loads(r.text)) self.logger.debug("source stream info:\n{0}", stream_source_info) if not stream_source_info: return urlnoproto = stream_source_info['localData']['videoServerUrl'] urlnoproto = update_scheme('https://', urlnoproto) performer = stream_source_info['performerData']['username'] hls_url = '{0}/hls/stream_{1}/playlist.m3u8'.format(urlnoproto, performer) if hls_url: self.logger.debug('HLS URL: {0}'.format(hls_url)) try: for s in HLSStream.parse_variant_playlist(self.session, hls_url, headers=headers).items(): yield s except Exception as e: if '404' in str(e): self.logger.error('Stream is currently offline or private') else: self.logger.error(str(e)) return
def parse_manifest(cls, session, url, **args): """ Attempt to parse a DASH manifest file and return its streams :param session: Streamlink session instance :param url: URL of the manifest file :return: a dict of name -> DASHStream instances """ ret = {} res = session.http.get(url, **args) url = res.url urlp = list(urlparse(url)) urlp[2], _ = urlp[2].rsplit("/", 1) mpd = MPD(session.http.xml(res, ignore_ns=True), base_url=urlunparse(urlp), url=url) video, audio = [], [] # Search for suitable video and audio representations for aset in mpd.periods[0].adaptationSets: if aset.contentProtection: raise PluginError("{} is protected by DRM".format(url)) for rep in aset.representations: if rep.mimeType.startswith("video"): video.append(rep) elif rep.mimeType.startswith("audio"): audio.append(rep) if not video: video = [None] if not audio: audio = [None] locale = session.localization locale_lang = locale.language lang = None available_languages = set() # if the locale is explicitly set, prefer that language over others for aud in audio: if aud and aud.lang: available_languages.add(aud.lang) try: if locale.explicit and aud.lang and Language.get(aud.lang) == locale_lang: lang = aud.lang except LookupError: continue if not lang: # filter by the first language that appears lang = audio[0] and audio[0].lang log.debug("Available languages for DASH audio streams: {0} (using: {1})".format(", ".join(available_languages) or "NONE", lang or "n/a")) # if the language is given by the stream, filter out other languages that do not match if len(available_languages) > 1: audio = list(filter(lambda a: a.lang is None or a.lang == lang, audio)) for vid, aud in itertools.product(video, audio): stream = DASHStream(session, mpd, vid, aud, **args) stream_name = [] if vid: stream_name.append("{:0.0f}{}".format(vid.height or vid.bandwidth_rounded, "p" if vid.height else "k")) if audio and len(audio) > 1: stream_name.append("a{:0.0f}k".format(aud.bandwidth)) ret['+'.join(stream_name)] = stream return ret
def __init__(self, url): super(Filmon, self).__init__(url) parsed = urlparse(self.url) if parsed.path.startswith("/channel/"): self.url = urlunparse(parsed._replace(path=parsed.path.replace("/channel/", "/tv/"))) self.api = FilmOnAPI(self.session)
def _get_streams(self): zdf_json = self.session.http.get( self.url, schema=validate.Schema( validate.transform(self._re_api_json.search), validate.any( None, validate.all(validate.get("json"), validate.parse_json(), { "apiToken": validate.text, "content": validate.url() }, validate.union_get("apiToken", "content"))))) if zdf_json is None: return apiToken, apiUrl = zdf_json headers = { "Accept": "application/vnd.de.zdf.v1.0+json;charset=UTF-8", "Api-Auth": "Bearer {0}".format(apiToken), "Referer": self.url } pApiUrl = urlparse(apiUrl) apiUrlBase = urlunparse( (pApiUrl.scheme, pApiUrl.netloc, "", "", "", "")) apiUrlPath = self.session.http.get( apiUrl, headers=headers, schema=validate.Schema( validate.parse_json(), { "mainVideoContent": { "http://zdf.de/rels/target": { "http://zdf.de/rels/streams/ptmd-template": validate.text } } }, validate.get(("mainVideoContent", "http://zdf.de/rels/target", "http://zdf.de/rels/streams/ptmd-template")), validate.transform(lambda template: template.format( playerId=self.PLAYER_ID).replace(" ", "")))) stream_request_url = url_concat(apiUrlBase, apiUrlPath) data = self.session.http.get( stream_request_url, headers=headers, schema=validate.Schema( validate.parse_json(), { "priorityList": [{ "formitaeten": validate.all( [{ "type": validate.text, "qualities": validate.all([{ "quality": validate.text, "audio": { "tracks": [{ "uri": validate.url() }] } }], validate.filter(lambda obj: obj[ "quality"] == "auto")) }], validate.filter(lambda obj: obj["type"] == "h264_aac_ts_http_m3u8_http")) }] }, validate.get("priorityList"))) for priority in data: for formitaeten in priority["formitaeten"]: for quality in formitaeten["qualities"]: for audio in quality["audio"]["tracks"]: for s in HLSStream.parse_variant_playlist( self.session, audio["uri"], headers=headers).items(): yield s