def _get_rtmp_streams(self, swfurl): if not RTMPStream.is_usable(self.session): raise NoStreamsError(self.url) self.logger.debug("Fetching RTMP stream info") res = http.get(swfurl) swf = swfdecompress(res.content) match = re.search("customURL[^h]+(https://.*?)\\\\", swf) if not match: raise NoStreamsError(self.url) res = http.get(match.group(1)) rtmp, playpath = rtmpparse(res.text) params = { "rtmp": rtmp, "pageUrl": self.url, "playpath": playpath, "live": True } match = re.search("file[^h]+(https?://.*?.swf)", swf) if match: params["swfUrl"] = match.group(1) return RTMPStream(self.session, params)
def _get_live_streams(self): stream_id = _id_map[self.channel_path] if stream_id == "ruv": qualities_rtmp = ["720p", "480p", "360p", "240p"] for i, quality in enumerate(qualities_rtmp): yield quality, RTMPStream( self.session, { "rtmp": RTMP_LIVE_URL.format(stream_id, i + 1), "pageUrl": self.url, "live": True }) qualities_hls = ["240p", "360p", "480p", "720p"] for i, quality_hls in enumerate(qualities_hls): yield quality_hls, HLSStream(self.session, HLS_RUV_LIVE_URL.format(i + 1)) else: yield "audio", RTMPStream( self.session, { "rtmp": RTMP_LIVE_URL.format(stream_id, 1), "pageUrl": self.url, "live": True }) yield "audio", HLSStream(self.session, HLS_RADIO_LIVE_URL.format(stream_id))
def _get_rtmp_streams(self): def clean_tag(tag): if tag[0] == "_": return tag[1:] else: return tag chansub = self._authenticate() url = self.StreamInfoURL.format(self.channelname) params = dict(b_id="true", group="", private_code="null", p=int(random.random() * 999999), channel_subscription=chansub, type="any") self.logger.debug("Fetching stream info") res = urlget(url, params=params) data = res.text # fix invalid xml data = re.sub("<(\d+)", "<_\g<1>", data) data = re.sub("</(\d+)", "</_\g<1>", data) streams = {} try: dom = xml.dom.minidom.parseString(data) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) nodes = dom.getElementsByTagName("nodes")[0] if len(nodes.childNodes) == 0: return streams swfurl = urlresolve(self.SWFURL) for node in nodes.childNodes: info = {} for child in node.childNodes: info[child.tagName] = self._get_node_text(child) if not ("connect" in info and "play" in info): continue stream = RTMPStream(self.session, { "rtmp": ("{0}/{1}").format(info["connect"], info["play"]), "swfVfy": swfurl, "live": True }) sname = clean_tag(node.tagName) if "token" in info: stream.params["jtv"] = info["token"] else: self.logger.warning("No token found for stream {0}, this stream may fail to play", sname) streams[sname] = stream return streams
def _get_streams(self): channelid = urlparse( self.url).path.rstrip("/").rpartition("/")[-1].lower() self.logger.debug("Fetching stream info") headers = {"Referer": self.url} options = dict(id=channelid) res = urlget(self.StreamInfoURL, headers=headers, params=options) json = res_json(res, "stream info JSON") if not isinstance(json, dict): raise PluginError("Invalid JSON response") if not ("rtmp" in json and "streamname" in json): raise NoStreamsError(self.url) if not RTMPStream.is_usable(self.session): raise PluginError( "rtmpdump is not usable and required by Owncast plugin") rtmp = json["rtmp"] playpath = json["streamname"] streams = {} streams["live"] = RTMPStream( self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfUrl": self.SWFURL, "playpath": playpath, "live": True }) return streams
def _get_rtmp_streams(self): chansub = self._authenticate() url = self.StreamInfoURL.format(self.channelname) params = dict( b_id="true", group="", private_code="null", p=int(random.random() * 999999), channel_subscription=chansub, type="any", ) self.logger.debug("Fetching stream info") res = urlget(url, params=params) json = res_json(res, "stream info JSON") if not isinstance(json, list): raise PluginError("Invalid JSON response") if len(json) == 0: raise NoStreamsError(self.url) streams = {} swfurl, swfhash, swfsize = self._verify_swf() for info in json: if not ("connect" in info and "play" in info and "type" in info): continue stream = RTMPStream( self.session, { "rtmp": ("{0}/{1}").format(info["connect"], info["play"]), "swfUrl": swfurl, "swfhash": swfhash, "swfsize": swfsize, "live": True, }, ) if "display" in info: sname = info["display"] else: sname = info["type"] if "token" in info: stream.params["jtv"] = info["token"] else: self.logger.warning("No token found for stream {0}, this stream may fail to play", sname) streams[sname] = stream return streams
def _get_rtmp_streams(self): chansub = self._authenticate() url = self.StreamInfoURL.format(self.channelname) params = dict(b_id="true", group="", private_code="null", p=int(random.random() * 999999), channel_subscription=chansub, type="any") self.logger.debug("Fetching stream info") res = urlget(url, params=params) json = res_json(res, "stream info JSON") if not isinstance(json, list): raise PluginError("Invalid JSON response") if len(json) == 0: raise NoStreamsError(self.url) streams = {} swfurl, swfhash, swfsize = self._verify_swf() for info in json: if not ("connect" in info and "play" in info and "type" in info): continue stream = RTMPStream( self.session, { "rtmp": ("{0}/{1}").format(info["connect"], info["play"]), "swfUrl": swfurl, "swfhash": swfhash, "swfsize": swfsize, "live": True }) if "display" in info: sname = info["display"] else: sname = info["type"] if "token" in info: stream.params["jtv"] = info["token"] else: self.logger.warning( "No token found for stream {0}, this stream may fail to play", sname) streams[sname] = stream return streams
def _get_streams(self): self.logger.debug("Fetching stream info") res = urlget(self.url) match = re.search("var current_channel = (.*);", res.text) if match: json = parse_json(match.group(1)) else: raise NoStreamsError(self.url) if not isinstance(json, dict): raise PluginError("Invalid JSON response") elif not "streams" in json: raise NoStreamsError(self.url) if not RTMPStream.is_usable(self.session): raise PluginError( "rtmpdump is not usable and required by Filmon plugin") match = re.search("var flash_config = (.*);", res.text) if match: config = parse_json(match.group(1)) if "streamer" in config: self.SWFURL = urljoin(self.SWFURL, config["streamer"]) streams = {} for stream in json["streams"]: if not ("url" in stream and "name" in stream): continue parsed = urlparse(stream["url"]) if not parsed.scheme.startswith("rtmp"): continue if parsed.query: app = "{0}?{1}".format(parsed.path[1:], parsed.query) else: app = parsed.path[1:] name = stream["quality"] streams[name] = RTMPStream( self.session, { "rtmp": stream["url"], "pageUrl": self.url, "swfUrl": self.SWFURL, "playpath": stream["name"], "app": app, "live": True }) return streams
def _get_streaminfo(self, channelname): def clean_tag(tag): if tag[0] == "_": return tag[1:] else: return tag metadata = self._get_metadata(channelname) randomp = int(random.random() * 999999) if "chansub_guid" in metadata: url = self.StreamInfoURLSub.format(channelname, randomp, metadata["chansub_guid"]) else: url = self.StreamInfoURL.format(channelname, randomp) data = urlget(url) # fix invalid xml data = re.sub(b"<(\d+)", b"<_\g<1>", data) data = re.sub(b"</(\d+)", b"</_\g<1>", data) streams = {} try: dom = xml.dom.minidom.parseString(data) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) nodes = dom.getElementsByTagName("nodes")[0] swfhash, swfsize = swfverify(self.SWFURL) for node in nodes.childNodes: info = {} for child in node.childNodes: info[child.tagName] = self._get_node_text(child) stream = RTMPStream({ "rtmp": ("{0}/{1}").format(info["connect"], info["play"]), "swfUrl": self.SWFURL, "swfhash": swfhash, "swfsize": swfsize, "live": 1 }) if "token" in info: stream.params["jtv"] = info["token"] sname = clean_tag(node.tagName) streams[sname] = stream return streams
def _get_streams(self): country_code = urlparse(self.url).netloc.split(".")[0] self.logger.debug("Fetching stream info") res = urlget(self.APIURL) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") elif not ("primary" in json or "secondary" in json): raise PluginError("Invalid JSON response") if not RTMPStream.is_usable(self.session): raise PluginError( "rtmpdump is not usable and required by Euronews plugin") streams = {} self.logger.debug("Euronews Countries:{0}", " ".join(json["primary"].keys())) if not (country_code in json["primary"] or country_code in json["secondary"]): res = urlget(self.GEOIPURL) geo = res_json(res) if isinstance(json, dict) and "country_code" in geo: country_code = geo["country_code"].lower() if not (country_code in json["primary"] or country_code in json["secondary"]): country_code = "en" else: country_code = "en" for site in ("primary", "secondary"): for quality in json[site][country_code]["rtmp_flash"]: stream = json[site][country_code]["rtmp_flash"][quality] name = quality + "k" if site == "secondary": name += "_alt" streams[name] = RTMPStream( self.session, { "rtmp": stream["server"], "playpath": stream["name"], "swfUrl": self.SWFURL, "live": True }) if len(streams) == 0: raise NoStreamsError(self.url) return streams
def _parse_live(self, json): streams = {} app = json["ingest"]["name"] playpath = json["name_seo"] i = 0 id = self._choose_server(json) for server in json["server"]: name = server["server"]["name"] height = str(json["current_video_height"])+"p" if id != i: height+="_alt_"+name streams[height] = RTMPStream(self.session, { "rtmp": "rtmp://"+server["server"]["hostname"], "app": app, "playpath": playpath, "pageUrl": self.url, "swfUrl": self.SWFURL, "live": True }) i += 1 return streams
def _get_streams(self): self.channelname = self._get_channel_name(self.url) if not self.channelname: raise NoStreamsError(self.url) streams = {} if RTMPStream.is_usable(self.session): try: rtmpstreams = self._get_rtmp_streams() streams.update(rtmpstreams) except PluginError as err: self.logger.error("Error when fetching RTMP stream info: {0}", str(err)) else: self.logger.warning("rtmpdump is not usable, only HLS streams will be available") try: hlsstreams = self._get_hls_streams() for name, stream in hlsstreams.items(): if name in streams: streams[name] = [streams[name], stream] else: streams[name] = stream except PluginError as err: self.logger.error("Error when fetching HLS stream info: {0}", str(err)) return streams
def _get_streams(self): self.logger.debug("Fetching stream info") res = urlget(self.url, params=dict(output="json")) if res.json is None: raise PluginError("No JSON data in stream info") streams = {} video = verifyjson(res.json, "video") videos = verifyjson(video, "videoReferences") for video in videos: if not ("url" in video and "playerType" in video): continue if video["playerType"] == "flash": if video["url"].startswith("rtmp"): stream = RTMPStream( self.session, { "rtmp": video["url"], "pageUrl": self.PageURL, "swfVfy": self.SWFURL, "live": True }) streams[str(video["bitrate"]) + "k"] = stream elif video["playerType"] == "ios": try: hlsstreams = HLSStream.parse_variant_playlist( self.session, video["url"]) streams.update(hlsstreams) except IOError as err: self.logger.warning("Failed to get variant playlist: {0}", err) return streams
def _get_sarpurinn_streams(self): res = http.get(self.url) match = _rtmp_url_re.search(res.text) if not match: yield token = match.group("id") status = match.group("status") extension = match.group("ext") date = match.group("date") if not date: date = "" if extension == "mp3": key = "audio" else: key = "576p" # HLS on Sarpurinn is currently only available on videos yield key, HLSStream( self.session, HLS_SARPURINN_URL.format(status, date, token, extension)) yield key, RTMPStream( self.session, { "rtmp": RTMP_SARPURINN_URL.format(status, date, token, extension), "pageUrl": self.url, "live": True })
def _get_streams(self): res = http.get(self.StreamURL, data={"from": "ongamenet"}) match = re.search("var stream = \"(.+?)\";", res.text) if not match: raise NoStreamsError(self.url) stream = match.group(1) match = re.search("var server = \"(.+?)\";", res.text) if not match: raise NoStreamsError(self.url) server = match.group(1) streams = {} streams["live"] = RTMPStream( self.session, { "rtmp": server, "playpath": stream, "swfUrl": self.SWFURL, "pageUrl": self.PageURL, "live": True, }) return streams
def _get_live_streams(self, params, swf_url): for key, quality in QUALITY_MAP.items(): key_url = "{0}URL".format(key) url = params.get(key_url) if not url: continue try: res = http.get(url, exception=IOError) except IOError: continue if quality == "hds": streams = HDSStream.parse_manifest(self.session, res.url) for name, stream in streams.items(): if key == "source": name += "+" yield name, stream elif res.text.startswith("rtmp"): match = _rtmp_re.match(res.text) if not match: continue stream = RTMPStream( self.session, { "rtmp": match.group("host"), "app": match.group("app"), "playpath": match.group("playpath"), "swfVfy": swf_url, "live": True }) yield quality, stream
def _get_streams(self): self.logger.debug("Fetching stream info") res = http.get(self.url) match = re.search("\"User_channelid\".+?value=\"(.+?)\"", res.text) if not match: raise NoStreamsError(self.url) headers = {"Referer": self.url} res = http.get(self.PlayerURL.format(match.group(1)), headers=headers) match = re.search("stream:\s+'(rtmp://.+?)'", res.text) if not match: raise NoStreamsError(self.url) rtmp = match.group(1) streams = {} streams["live"] = RTMPStream(self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfVfy": self.SWFURL, "live": True }) return streams
def _get_streams(self): info = http.get(self.url, schema=_schema) if not info: return headers = {"Referer": self.url} res = http.get(info["token_url"], headers=headers) token = http.json(res, schema=_token_schema) parsed = urlparse(info["rtmp_url"]) if parsed.query: app = "{0}?{1}".format(parsed.path[1:], parsed.query) else: app = parsed.path[1:] params = { "rtmp": info["rtmp_url"], "app": app, "pageUrl": self.url, "swfVfy": info["swf_url"], "playpath": info["rtmp_playpath"], "token": token, "live": True } stream = RTMPStream(self.session, params) return dict(live=stream)
def _get_live_streams(self): streams = defaultdict(list) if RTMPStream.is_usable(self.session): try: for name, stream in self._get_desktop_streams().items(): streams[name].append(stream) except PluginError as err: self.logger.error("Error when fetching desktop streams: {0}", err) except NoStreamsError: pass else: self.logger.warning("rtmpdump is not usable, " "only mobile streams may be available") try: for name, stream in self._get_mobile_streams().items(): # Justin.tv streams have a iphone prefix, so let's # strip it to keep it consistent with Twitch. name = name.replace("iphone", "") streams[name].append(stream) except PluginError as err: self.logger.error("Error when fetching mobile streams: {0}", err) except NoStreamsError: pass return streams
def _get_streams(self): self.logger.debug("Fetching stream info") res = urlget(self.url) match = re.search( "flashplayer: \"(.+.swf)\".+streamer: \"(.+)\".+file: \"(.+).flv\"", res.text, re.DOTALL) if not match: raise NoStreamsError(self.url) rtmp = match.group(2) playpath = match.group(3) swfurl = match.group(1) streams = {} streams["live"] = RTMPStream(self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfVfy": swfurl, "playpath": playpath, "live": True }, redirect=True) return streams
def _get_streams(self): if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Filmon plugin") self.logger.debug("Fetching stream info") self.rsession = requests.session() res = urlget(self.url, session=self.rsession) match = re.search("movie_id=(\d+)", res.text) if match: return self._get_vod_stream(match.group(1)) match = re.search("/channels/(\d+)/extra_big_logo.png", res.text) if not match: return channel_id = match.group(1) streams = {} for quality in ("low", "high"): try: streams[quality] = self._get_stream(channel_id, quality) except NoStreamsError: pass return streams
def _get_vod_stream(self, movie_id): res = http.get(VODINFO_URL.format(movie_id), headers=AJAX_HEADERS) json = http.json(res) json = json and json.get("data") json = json and json.get("streams") if not json: raise NoStreamsError(self.url) streams = {} for quality in ("low", "high"): stream = json.get(quality) if not stream: continue rtmp = stream.get("url") app = self._get_rtmp_app(rtmp) if not app: continue playpath = stream.get("name") if ".mp4" in playpath: playpath = "mp4:" + playpath streams[quality] = RTMPStream( self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfUrl": SWF_URL, "playpath": playpath, "app": app, }) return streams
def _get_stream(self, channel_id, quality): params = dict(channel_id=channel_id, quality=quality) res = http.post(CHINFO_URL, data=params, headers=AJAX_HEADERS) json = http.json(res) if not json: raise NoStreamsError(self.url) rtmp = json.get("serverURL") playpath = json.get("streamName") if not (rtmp and playpath): raise NoStreamsError(self.url) app = self._get_rtmp_app(rtmp) if not app: raise NoStreamsError(self.url) return RTMPStream( self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfUrl": SWF_URL, "playpath": playpath, "app": app, "live": True })
def _get_streams(self): if not RTMPStream.is_usable(self.session): raise PluginError( "rtmpdump is not usable and required by Filmon plugin") self.logger.debug("Fetching stream info") res = http.get(self.url) match = re.search("movie_id=(\d+)", res.text) if match: return self._get_vod_stream(match.group(1)) match = re.search("/channels/(\d+)/extra_big_logo.png", res.text) if not match: return channel_id = match.group(1) streams = {} for quality in ("low", "high"): try: streams[quality] = self._get_stream(channel_id, quality) except NoStreamsError: pass return streams
def _get_streams(self): res = http.get(self.url, schema=_schema) if not res: return owner = res["vars"]["owner"] token = res["vars"].get("token", "null") swf_url = res["swf"] # Check if the stream is online res = http.get(CHANNEL_DETAILS_URI.format(owner, token)) channel_details = http.json(res, schema=_channel_details_schema) if not channel_details["channel"]["live"]: return stream_ip = http.get(REDIRECT_SERVICE_URI.format(owner)).text streams = {} streams["live"] = RTMPStream( self.session, { "rtmp": RTMP_URL.format(stream_ip, channel_details["channel"]["slug"]), "pageUrl": self.url, "swfUrl": urljoin(self.url, swf_url), "live": True }) return streams
def _get_streams(self): channelname = urlparse( self.url).path.rstrip("/").rpartition("/")[-1].lower() self.logger.debug("Fetching stream info") headers = {"Referer": self.url} res = urlget(self.PlayerURL.format(channelname), headers=headers) match = re.search("'FlashVars', '(id=\d+)&", res.text) if not match: raise NoStreamsError(self.url) channelname += "?" + match.group(1) res = urlget(self.BalancerURL, headers=headers) match = re.search("redirect=(.+)", res.text) if not match: raise PluginError( "Error retrieving RTMP address from loadbalancer") rtmp = match.group(1) streams = {} streams["live"] = RTMPStream( self.session, { "rtmp": "rtmp://{0}/live/{1}".format(rtmp, channelname), "pageUrl": self.url, "swfVfy": self.SWFURL, "conn": "S:OK", "live": True }) return streams
def _get_streams(self): channelid = urlparse(self.url).path.rstrip("/").rpartition("/")[-1].lower() self.logger.debug("Fetching stream info") headers = {"Referer": self.url} options = dict(id=channelid) res = urlget(self.StreamInfoURL, headers=headers, params=options) json = res_json(res, "stream info JSON") if not isinstance(json, dict): raise PluginError("Invalid JSON response") if not ("rtmp" in json and "streamname" in json): raise NoStreamsError(self.url) if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Owncast plugin") rtmp = json["rtmp"] playpath = json["streamname"] streams = {} streams["live"] = RTMPStream( self.session, {"rtmp": rtmp, "pageUrl": self.url, "swfUrl": self.SWFURL, "playpath": playpath, "live": True} ) return streams
def _get_rtmp_streams(self, channelname): options = dict(l="info", a="xmlClipPath", clip_id=channelname, rid=time()) res = urlget(self.APIURL, params=options) dom = res_xml(res) rtmpurl = dom.getElementsByTagName("url") rtmp = None if len(rtmpurl) > 0: rtmp = get_node_text(rtmpurl[0]) else: raise PluginError( ("No RTMP Streams found on URL {0}").format(self.url)) rtmplist = {} rtmplist["live"] = RTMPStream(self.session, { "rtmp": rtmp, "swfVfy": self.SWFURL, "live": True }) return rtmplist
def _create_rtmp_stream(self, cdn, stream_name): parsed = urlparse(cdn) options = dict(rtmp=cdn, app=parsed.path[1:], playpath=stream_name, pageUrl=self.url, swfUrl=SWF_URL, live=True) return RTMPStream(self.session, options)
def _get_streams(self): if not RTMPStream.is_usable(self.session): self.logger.warning("rtmpdump is not usable, only HDS streams will be available") self.logger.debug("Fetching stream info") match = re.search("/\w*/(live|video)*/(\d+)", self.url) if not match: return stream_id = match.group(2) res = http.get(API_URL, params=dict(ak="web", id=stream_id)) root = parse_xml(res.text.encode("utf8")) streams = {} for formitaet in root.iter('formitaet'): url = formitaet.find('url').text quality = formitaet.find('quality').text if formitaet.get('basetype') == "h264_aac_f4f_http_f4m_http": hds_streams = HDSStream.parse_manifest(self.session, url) streams.update(hds_streams) elif formitaet.get('basetype') == 'h264_aac_mp4_rtmp_zdfmeta_http': streams[quality] = RTMPStream(self.session, { "rtmp": self._get_stream(url), "pageUrl": self.url, }) return streams
def _get_live_streams(self, *args, **kwargs): streams = defaultdict(list) if RTMPStream.is_usable(self.session): try: for name, stream in self._get_desktop_streams(*args, **kwargs).items(): streams[name].append(stream) except PluginError as err: self.logger.error("Error when fetching desktop streams: {0}", err) except NoStreamsError: pass else: self.logger.warning("rtmpdump is required to access the desktop " "streams, but it could not be found") try: for name, stream in self._get_mobile_streams(*args, **kwargs).items(): # Justin.tv streams have a iphone prefix, so let's # strip it to keep it consistent with Twitch. name = name.replace("iphone", "") streams[name].append(stream) except PluginError as err: self.logger.error("Error when fetching mobile streams: {0}", err) except NoStreamsError: pass return streams
def _get_streams(self): self.channelname = self._get_channel_name(self.url) if not self.channelname: raise NoStreamsError(self.url) streams = {} if RTMPStream.is_usable(self.session): try: rtmpstreams = self._get_rtmp_streams() streams.update(rtmpstreams) except PluginError as err: self.logger.error("Error when fetching RTMP stream info: {0}", str(err)) else: self.logger.warning("rtmpdump is not usable, only HLS streams will be available") try: hlsstreams = self._get_hls_streams() if len(streams) > 0: hlssuffix = "_hls" else: hlssuffix = "" for name, stream in hlsstreams.items(): streams[name + hlssuffix] = stream except PluginError as err: self.logger.error("Error when fetching HLS stream info: {0}", str(err)) return streams
def _get_streams(self, type): self.channelname = self._get_channel_name(self.url) if not self.channelname: raise NoStreamsError(self.url) streams = {} if type in (None, StreamType.RTMP): if RTMPStream.is_usable(self.session): try: rtmpstreams = self._get_rtmp_streams() streams.update(rtmpstreams) except PluginError as err: self.logger.error("Error when fetching RTMP stream info: {0}", str(err)) else: self.logger.warning("rtmpdump is not usable, only HLS streams will be available") if type in (None, StreamType.HLS): try: hlsstreams = self._get_hls_streams() if len(streams) > 0: hlssuffix = "_hls" else: hlssuffix = "" for name, stream in hlsstreams.items(): streams[name + hlssuffix] = stream except PluginError as err: self.logger.error("Error when fetching HLS stream info: {0}", str(err)) return streams
def _create_stream(self, cdn, streamname): url = "{0}/{1}".format(cdn, streamname) options = dict(rtmp=url, pageUrl=self.url, swfUrl=self.SWFURL, live=True) return RTMPStream(self.session, options)
def _get_streams(self): self.logger.debug("Fetching stream info") res = urlget(self.url) match = re.search( "flashplayer: \"(.+.swf)\".+streamer: \"(.+)\".+file: \"(.+).flv\"", res.text, re.DOTALL) if not match: raise NoStreamsError(self.url) params = { "rtmp": match.group(2), "pageUrl": self.url, "swfVfy": match.group(1), "playpath": match.group(3), "live": True } match = re.search("(http(s)?://.+/server.php\?id=\d+)", res.text) if match: token_url = match.group(1) res = res_json(urlget(token_url, headers=dict(Referer=self.url))) token = res.get("token") if token: params["token"] = token streams = {} streams["live"] = RTMPStream(self.session, params) return streams
def _create_stream(self, stream, is_live): stream_name = "{0}p".format(stream["height"]) stream_type = stream["mediaType"] stream_url = stream["url"] if stream_type in ("hls", "mp4"): if urlparse(stream_url).path.endswith("m3u8"): try: streams = HLSStream.parse_variant_playlist( self.session, stream_url) # TODO: Replace with "yield from" when dropping Python 2. for stream in streams.items(): yield stream except IOError as err: self.logger.error("Failed to extract HLS streams: {0}", err) else: yield stream_name, HTTPStream(self.session, stream_url) elif stream_type == "rtmp": params = { "rtmp": stream["streamer"], "playpath": stream["url"], "swfVfy": SWF_URL, "pageUrl": self.url, } if is_live: params["live"] = True else: params["playpath"] = "mp4:{0}".format(params["playpath"]) stream = RTMPStream(self.session, params) yield stream_name, stream
def _get_streams(self): match = re.search("/v/(\d+)", self.url) if not match: return vid = match.group(1) params = dict(client_name="Bambuser AS2", context="b_broadcastpage", raw_user_input=1, api_key="005f64509e19a868399060af746a00aa", vid=vid, r=random.random()) self.logger.debug("Fetching stream info") res = http.get(PLAYER_URL, params=params) json = http.json(res) error = json and json.get("errorCode") if error: error = error and json.get("error") self.logger.error(error) return json = verifyjson(json, "result") playpath = verifyjson(json, "id") url = verifyjson(json, "url") (width, height) = verifyjson(json, "size").split("x") name = "{0}p".format(height) parsed = urlparse(url) streams = {} if parsed.scheme.startswith("rtmp"): if not RTMPStream.is_usable(self.session): raise PluginError( "rtmpdump is not usable and required by Bambuser plugin") streams[name] = RTMPStream( self.session, { "rtmp": url, "playpath": playpath, "pageUrl": self.url, "live": True }) elif parsed.scheme == "http": streams[name] = HTTPStream(self.session, url) return streams
def _get_streams(self): params = parse_qsd(urlparse(self.url).query) if not 'watch' in params: raise NoStreamsError(self.url) channel = params['watch'] if not RTMPStream.is_usable(self.session): raise PluginError( "rtmpdump is not usable but required by Picarto plugin") streams = {} streams["live"] = RTMPStream( self.session, { "rtmp": "rtmp://199.189.86.17/dsapp/{0}.flv".format(channel), "pageUrl": self.url, "live": True }) return streams
def _get_rtmp_streams(self, text): match = re.search("streamer=(rtmp://.+?)&", text) if not match: raise PluginError( ("No RTMP streamer found on URL {0}").format(self.url)) rtmp = match.group(1) match = re.search("<meta content=\"(http://.+?\.swf)\?", text) if not match: self.logger.warning( "Failed to get player SWF URL location on URL {0}", self.url) else: self.SWFURL = match.group(1) self.logger.debug("Found player SWF URL location {0}", self.SWFURL) match = re.search("<meta content=\"(.+)\" name=\"item-id\" />", text) if not match: raise PluginError( ("Missing channel item-id on URL {0}").format(self.url)) res = http.get(self.APIURL.format(match.group(1), time()), params=dict(output="json")) json = http.json(res) if not isinstance(json, list): raise PluginError("Invalid JSON response") rtmplist = {} for jdata in json: if "stream_name" not in jdata or "type" not in jdata: continue if "rtmp" not in jdata["type"]: continue playpath = jdata["stream_name"] if "token" in jdata and jdata["token"]: playpath += jdata["token"] if len(json) == 1: stream_name = "live" else: stream_name = jdata["stream_name"] rtmplist[stream_name] = RTMPStream( self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfVfy": self.SWFURL, "playpath": playpath, "live": True }) return rtmplist
def _create_rtmp_stream(self, rtmp, playpath, live): return RTMPStream( self.session, { "rtmp": rtmp, "playpath": playpath, "pageUrl": self.url, "swfVfy": SWF_URL, "live": live })
def _get_streams(self): self.logger.debug("Fetching stream info") res = urlget(self.url) match = re.search("var current_channel = (.*);", res.text) if match: json = parse_json(match.group(1)) else: raise NoStreamsError(self.url) if not isinstance(json, dict): raise PluginError("Invalid JSON response") elif not "streams" in json: raise NoStreamsError(self.url) if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Filmon plugin") match = re.search("var flash_config = (.*);", res.text) if match: config = parse_json(match.group(1)) if "streamer" in config: self.SWFURL = urljoin(self.SWFURL, config["streamer"]) streams = {} for stream in json["streams"]: if not ("url" in stream and "name" in stream): continue parsed = urlparse(stream["url"]) if not parsed.scheme.startswith("rtmp"): continue if parsed.query: app = "{0}?{1}".format(parsed.path[1:], parsed.query) else: app = parsed.path[1:] name = stream["quality"] streams[name] = RTMPStream(self.session, { "rtmp": stream["url"], "pageUrl": self.url, "swfUrl": self.SWFURL, "playpath": stream["name"], "app": app, "live": True }) return streams
def _get_streams_from_rtmp(self): password = self.options.get("password") module_info = self._get_module_info("channel", self.channel_id, password) if not module_info: raise NoStreamsError(self.url) providers = module_info.get("stream") if providers == "offline": raise NoStreamsError(self.url) elif not isinstance(providers, list): raise PluginError("Invalid stream info: {0}".format(providers)) streams = {} for provider in filter(valid_provider, providers): provider_url = provider.get("url") provider_name = provider.get("name") provider_streams = provider.get("streams") for stream_index, stream_info in enumerate(provider_streams): stream = None stream_height = int(stream_info.get("height", 0)) stream_name = stream_info.get("description") if not stream_name: if stream_height: if not stream_info.get("isTranscoded"): stream_name = "{0}p+".format(stream_height) else: stream_name = "{0}p".format(stream_height) else: stream_name = "live" if stream_name in streams: provider_name_clean = provider_name.replace("uhs_", "") stream_name += "_alt_{0}".format(provider_name_clean) if provider_name.startswith("uhs_"): stream = UHSStream(self.session, self.channel_id, self.url, provider_name, stream_index, password) elif (provider_url.startswith("rtmp") and RTMPStream.is_usable(self.session)): playpath = stream_info.get("streamName") stream = self._create_rtmp_stream(provider_url, playpath) if stream: streams[stream_name] = stream return streams
def _get_streams(self): # If email option given, try to login if self.options.get("email"): res = http.get(self.LOGINPAGEURL) match = re.search('<meta content="([^"]+)" name="csrf-token"', res.text) if not match: raise PluginError("Missing CSRF Token: " + self.LOGINPAGEURL) csrf_token = match.group(1) email = self.options.get("email") password = self.options.get("password") res = http.post( self.LOGINPOSTURL, data={ "authenticity_token": csrf_token, "channel_id": "", "commit": "Login", "plan_id": "", "session[email]": email, "session[password]": password, "utf8": "\xE2\x9C\x93", # Check Mark Character }, ) self.logger.debug("Login account info: {0}", res.text) result = http.json(res) if result.get("email", "no-mail") != email: raise PluginError("Invalid account") res = http.get(self.url) streams = {} if RTMPStream.is_usable(self.session): try: rtmpstreams = self._get_rtmp_streams(res.text) streams.update(rtmpstreams) except PluginError as err: self.logger.error("Error when fetching RTMP stream info: {0}", str(err)) else: self.logger.warning("rtmpdump is not usable, only HLS streams will be available") try: hlsstreams = self._get_hls_streams(res.text) streams.update(hlsstreams) except PluginError as err: self.logger.error("Error when fetching HLS stream info: {0}", str(err)) return streams
def _get_streams(self): params = parse_qsd(urlparse(self.url).query) if not 'watch' in params: raise NoStreamsError(self.url) channel = params['watch'] if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable but required by Picarto plugin") streams = {} streams["live"] = RTMPStream(self.session, { "rtmp": "rtmp://199.189.86.17/dsapp/{0}.flv".format(channel), "pageUrl": self.url, "live": True }) return streams
def _get_streams(self): country_code = urlparse(self.url).netloc.split(".")[0] self.logger.debug("Fetching stream info") res = http.get(self.APIURL) json = http.json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") elif not ("primary" in json or "secondary" in json): raise PluginError("Invalid JSON response") if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Euronews plugin") streams = {} self.logger.debug("Euronews Countries:{0}", " ".join(json["primary"].keys())) if not (country_code in json["primary"] or country_code in json["secondary"]): res = http.get(self.GEOIPURL) geo = http.json(res) if isinstance(json, dict) and "country_code" in geo: country_code = geo["country_code"].lower() if not (country_code in json["primary"] or country_code in json["secondary"]): country_code = "en" else: country_code = "en" for site in ("primary", "secondary"): for quality in json[site][country_code]["rtmp_flash"]: stream = json[site][country_code]["rtmp_flash"][quality] name = quality + "k" if site == "secondary": name += "_alt" streams[name] = RTMPStream(self.session, { "rtmp": stream["server"], "playpath" : stream["name"], "swfUrl": self.SWFURL, "live": True }) if len(streams) == 0: raise NoStreamsError(self.url) return streams
def _get_streams_from_amf(self): if not RTMPStream.is_usable(self.session): raise NoStreamsError(self.url) res = urlget(AMF_URL.format(self.channel_id)) try: packet = AMFPacket.deserialize(BytesIO(res.content)) except (IOError, AMFError) as err: raise PluginError("Failed to parse AMF packet: {0}".format(err)) for message in packet.messages: if message.target_uri == "/1/onResult": result = message.value break else: raise PluginError("No result found in AMF packet") streams = {} stream_name = result.get("streamName") if stream_name: cdn = result.get("cdnUrl") or result.get("fmsUrl") if cdn: stream = self._create_rtmp_stream(cdn, stream_name) if "videoCodec" in result and result["videoCodec"]["height"] > 0: stream_name = "{0}p".format(int(result["videoCodec"]["height"])) else: stream_name = "live" streams[stream_name] = stream else: self.logger.warning("Missing cdnUrl and fmsUrl from result") stream_versions = result.get("streamVersions") if stream_versions: for version, info in stream_versions.items(): stream_version_cdn = info.get("streamVersionCdn", {}) for name, cdn in filter(valid_cdn, stream_version_cdn.items()): stream = self._create_rtmp_stream(cdn["cdnStreamUrl"], cdn["cdnStreamName"]) stream_name = "live_alt_{0}".format(name) streams[stream_name] = stream return streams
def _get_streams(self): match = re.search("/v/(\d+)", self.url) if not match: return vid = match.group(1) params = dict(client_name="Bambuser AS2", context="b_broadcastpage", raw_user_input=1, api_key="005f64509e19a868399060af746a00aa", vid=vid, r=random.random()) self.logger.debug("Fetching stream info") res = http.get(PLAYER_URL, params=params) json = http.json(res) error = json and json.get("errorCode") if error: error = error and json.get("error") self.logger.error(error) return json = verifyjson(json, "result") playpath = verifyjson(json, "id") url = verifyjson(json, "url") (width, height) = verifyjson(json, "size").split("x") name = "{0}p".format(height) parsed = urlparse(url) streams = {} if parsed.scheme.startswith("rtmp"): if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Bambuser plugin") streams[name] = RTMPStream(self.session, { "rtmp": url, "playpath": playpath, "pageUrl": self.url, "live": True }) elif parsed.scheme == "http": streams[name] = HTTPStream(self.session, url) return streams
def _get_streams(self): if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Filmon_us plugin") streams = {} try: # history video if "filmon.us/history" in self.url or "filmon.us/video/history/hid" in self.url: streams['default'] = self._get_history() # uploaded video elif "filmon.us/video" in self.url: streams['default'] = self._get_stream_upload() # live video else: streams['default'] = self._get_stream_live() except NoStreamsError: pass return streams
def _get_streams(self): res = urlget(self.url) streams = {} if RTMPStream.is_usable(self.session): try: rtmpstreams = self._get_rtmp_streams(res.text) streams.update(rtmpstreams) except PluginError as err: self.logger.error("Error when fetching RTMP stream info: {0}", str(err)) else: self.logger.warning("rtmpdump is not usable, only HLS streams will be available") try: hlsstreams = self._get_hls_streams(res.text) streams.update(hlsstreams) except PluginError as err: self.logger.error("Error when fetching HLS stream info: {0}", str(err)) return streams
def _get_streams(self): channelname = urlparse(self.url).path.rstrip("/").rpartition("/")[-1].lower() streams = {} if RTMPStream.is_usable(self.session): try: rtmpstreams = self._get_rtmp_streams(channelname) streams.update(rtmpstreams) except PluginError as err: self.logger.error("Error when fetching RTMP stream info: {0}", str(err)) else: self.logger.warning("rtmpdump is not usable, only HLS streams will be available") try: hlsstreams = self._get_hls_streams(channelname) streams.update(hlsstreams) except PluginError as err: self.logger.error("Error when fetching HLS stream info: {0}", str(err)) return streams
def _get_streams(self): self.channelname = self._get_channel_name(self.url) if not self.channelname: raise NoStreamsError(self.url) streams = {} if RTMPStream.is_usable(self.session): try: rtmpstreams = self._get_rtmp_streams() for name, stream in rtmpstreams.items(): if "iphone" in name: name = name.replace("iphone", "mobile_") streams[name] = stream except PluginError as err: self.logger.error("Error when fetching RTMP stream info: {0}", str(err)) else: self.logger.warning("rtmpdump is not usable, only HLS streams will be available") try: hlsstreams = self._get_hls_streams() for name, stream in hlsstreams.items(): if name in ("high", "low"): name = "mobile_{0}".format(name) if "iphone" in name: name = name.replace("iphone", "mobile_") if name in streams: streams[name] = [streams[name], stream] else: streams[name] = stream except PluginError as err: self.logger.error("Error when fetching HLS stream info: {0}", str(err)) return streams
def _get_streams(self): if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Hashd plugin") self.logger.debug("Fetching stream info") res = http.get(self.url.rstrip("/").lower()+".json?first=true") json = http.json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") elif not ("live" in json or "file" in json): raise PluginError("Invalid JSON response") if "file" in json: streams = self._parse_vod(json) elif json["live"]: streams = self._parse_live(json) else: raise NoStreamsError(self.url) return streams
def _get_streams(self): if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Furstream plugin") self.logger.debug("Fetching stream info") res = http.get(self.url) match = re.search("rtmp://(?:(?!\").)*", res.text) if match: self.logger.debug("Stream URL: " + match.group(0)) rtmp = match.group(0) else: return streams = {} streams["live"] = RTMPStream(self.session, { "rtmp": rtmp, "pageUrl": self.url, "live": True }) return streams
def _get_live_streams(self): self.channel_id = self._get_channel_id(self.url) if not self.channel_id: raise NoStreamsError(self.url) streams = defaultdict(list) if not RTMPStream.is_usable(self.session): self.logger.warning("rtmpdump is not usable. " "Not all streams may be available.") if HAS_LIBRTMP: desktop_streams = self._get_streams_from_rtmp else: self.logger.warning("python-librtmp is not installed. " "Not all streams may be available.") desktop_streams = self._get_streams_from_amf try: for name, stream in desktop_streams().items(): streams[name].append(stream) except PluginError as err: self.logger.error("Unable to fetch desktop streams: {0}", err) except NoStreamsError: pass try: mobile_streams = self._get_hls_streams(wait_for_transcode=not streams) for name, stream in mobile_streams.items(): streams[name].append(stream) except PluginError as err: self.logger.error("Unable to fetch mobile streams: {0}", err) except NoStreamsError: pass return streams
def _get_streams(self): if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Cybergame plugin") if "/videos/" in self.url: return self._get_vod_streams() self.logger.debug("Fetching live stream info") res = http.get(self.url) match = re.search("channel=([^\"]+)", res.text) if not match: raise NoStreamsError(self.url) channelname = match.group(1) res = http.get(CONFIG_URL, params=dict(c=channelname, ports="y")) json = http.json(res) servers = json.get("servers") if not servers: raise NoStreamsError(self.url) alternative = "" streams = {} for server in servers: srv, port = server.split(":") params = dict(channel=channelname, server=srv, port=port) tmpstreams = self._get_rtmp_streams(params, alternative) streams.update(tmpstreams) if not alternative: alternative = "_alt" else: break return streams
def _get_streaminfo(self, channelname): def clean_tag(tag): if tag[0] == "_": return tag[1:] else: return tag chansub = None if self.options.get("cookie") is not None: self.logger.debug("Attempting to authenticate using cookie") metadata = self._get_metadata(channelname) chansub = metadata["access_guid"] if "login" in metadata and metadata["login"] is not None: self.logger.debug("Successfully logged in as {0}", metadata["login"]) randomp = int(random.random() * 999999) url = self.StreamInfoURL.format(channelname, randomp, chansub) self.logger.debug("Fetching stream info") data = urlget(url) # fix invalid xml data = re.sub(b"<(\d+)", b"<_\g<1>", data) data = re.sub(b"</(\d+)", b"</_\g<1>", data) streams = {} try: dom = xml.dom.minidom.parseString(data) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) nodes = dom.getElementsByTagName("nodes")[0] self.logger.debug("Verifying SWF: {0}", self.SWFURL) swfhash, swfsize = swfverify(self.SWFURL) for node in nodes.childNodes: info = {} for child in node.childNodes: info[child.tagName] = self._get_node_text(child) stream = RTMPStream(self.session, { "rtmp": ("{0}/{1}").format(info["connect"], info["play"]), "swfUrl": self.SWFURL, "swfhash": swfhash, "swfsize": swfsize, "live": True }) sname = clean_tag(node.tagName) if "token" in info: stream.params["jtv"] = info["token"] else: self.logger.warning("No token found for stream {0}, this stream may fail to play", sname) streams[sname] = stream return streams
def _get_streams(self): channelid = self._get_channel_id(self.url) if not channelid: raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") res = urlget(self.AMFURL.format(channelid)) try: packet = AMFPacket.deserialize(BytesIO(res.content)) except (IOError, AMFError) as err: raise PluginError(("Failed to parse AMF packet: {0}").format(str(err))) result = None for message in packet.messages: if message.target_uri == "/1/onResult": result = message.value break if not result: raise PluginError("No result found in AMF packet") streams = {} if RTMPStream.is_usable(self.session) and "streamName" in result: if "cdnUrl" in result: cdn = result["cdnUrl"] elif "fmsUrl" in result: cdn = result["fmsUrl"] else: self.logger.warning("Missing cdnUrl and fmsUrl from result") return streams if "videoCodec" in result and result["videoCodec"]["height"] > 0: streamname = "{0}p".format(int(result["videoCodec"]["height"])) else: streamname = "live" streams[streamname] = self._create_stream(cdn, result["streamName"]) if RTMPStream.is_usable(self.session) and "streamVersions" in result: for version, info in result["streamVersions"].items(): if "streamVersionCdn" in info: for name, cdn in info["streamVersionCdn"].items(): if "cdnStreamUrl" in cdn and "cdnStreamName" in cdn: cdnname = "live_alt_{0}".format(name) streams[cdnname] = self._create_stream(cdn["cdnStreamUrl"], cdn["cdnStreamName"]) # On some channels the AMF API will not return any streams, # attempt to access the HLS playlist directly instead. # # HLS streams are created on demand, so we may have to wait # for a transcode to be started. attempts = 10 playlist_url = result.get("liveHttpUrl", self.HLSPlaylistURL.format(channelid)) while attempts: try: hls_streams = HLSStream.parse_variant_playlist(self.session, playlist_url) streams.update(hls_streams) except IOError: # Channel is probably offline break if streams: break attempts -= 1 sleep(3) return streams
def _get_streams(self): channelname = urlparse(self.url).path.rstrip("/").rpartition("/")[-1].lower() self.logger.debug("Fetching stream info") headers = { "Referer": self.SWFURL } form = dict(cid=channelname, watchTime="0", firstConnect="1", ip="NaN") res = urlopen(self.APIURL, data=form, headers=headers) params = parse_qsd(res.text) if "0" in params and int(params["0"]) <= 0: raise PluginError("Server refused to send required parameters.") rtmp = params["10"] playpath = params["11"] multibitrate = int(params["20"]) premiumuser = params["5"] blocktype = int(params["13"]) if blocktype != 0: if blocktype == 1: blocktime = params["14"] reconnectiontime = params["16"] msg = ("You have crossed free viewing limit. ", "You have been blocked for %s minutes. " % blocktime, "Try again in %s minutes." % reconnectiontime) raise PluginError(msg) elif blocktype == 11: raise PluginError("No free slots available.") if "73" in params: token = params["73"] else: raise PluginError("Server seems busy, please try after some time.") if not RTMPStream.is_usable(self.session): raise PluginError("rtmpdump is not usable and required by Weeb plugin") streams = {} stream_name = "sd" if multibitrate: streams[stream_name] = RTMPStream(self.session, { "rtmp": "{0}/{1}".format(rtmp, playpath), "pageUrl": self.url, "swfVfy": self.SWFURL, "weeb": token, "live": True }) playpath += "HI" stream_name = "hd" streams[stream_name] = RTMPStream(self.session, { "rtmp": "{0}/{1}".format(rtmp, playpath), "pageUrl": self.url, "swfVfy": self.SWFURL, "weeb": token, "live": True }) return streams