def _choose_server(self, json): res = urlget(self.GEOIPURL) loc = res_json(res) loc = [loc["latitude"], loc["longitude"]] sel_dist = float("inf") i = 0 primary = -1 secondary = -1 for server in json["server"]: res = urlget(self.GEOURL+server["server"]["name"]+"&sensor=false") cord = res_json(res) cord = [cord["results"][0]["geometry"]["location"]["lat"], cord["results"][0]["geometry"]["location"]["lng"]] cur_dist = self._distance(loc, cord) if cur_dist < sel_dist: sel_dist = cur_dist if server["server"]["used"] < 90: # nearest server with load < 90% primary = i else: # nearest server with load > 90% secondary = i i += 1 if primary == -1: # if all servers have load over 90% use nearest one return secondary return primary
def _get_streams(self): self.logger.debug("Fetching stream info") media_is_live = 0 match = re.search(r".*hitbox.tv/([^/]*)/?(\d+)?", self.url) if not match: raise NoStreamsError(self.url) stream_name, media_id = match.groups() if stream_name != "video": res = urlget(LIVE_API.format(stream_name)) json = res_json(res) livestream = verifyjson(json, "livestream") media_id = verifyjson(livestream[0], "media_id") media_is_live = int(verifyjson(livestream[0], "media_is_live")) if not media_is_live: raise NoStreamsError(self.url) media_type = "live" if media_is_live else "video" res = urlget(PLAYER_API.format(media_type, media_id)) json = res_json(res) clip = verifyjson(json, "clip") live = verifyjson(clip, "live") bitrates = verifyjson(clip, "bitrates") streams = {} if live: for bitrate in bitrates: connection_provider = clip.get("connectionProvider", "clustering") plugins = verifyjson(json, "plugins") provider_plugin = verifyjson(plugins, connection_provider) swf = verifyjson(provider_plugin, "url") rtmp = verifyjson(provider_plugin, "netConnectionUrl") quality = self._get_quality(verifyjson(bitrate, "label")) url = verifyjson(bitrate, "url") streams[quality] = RTMPStream( self.session, { "rtmp": rtmp, "pageUrl": self.url, "playpath": url, "swfVfy": SWF_BASE + swf, "live": True }) else: for bitrate in bitrates: base_url = verifyjson(clip, "baseUrl") url = verifyjson(bitrate, "url") quality = self._get_quality(verifyjson(bitrate, "label")) streams[quality] = HTTPStream(self.session, base_url + "/" + url) return streams
def _get_streams(self): self.logger.debug("Fetching stream info") media_is_live = 0 match = re.search(r".*hitbox.tv/([^/]*)/?(\d+)?", self.url) if not match: raise NoStreamsError(self.url) stream_name, media_id = match.groups() if stream_name != "video": res = urlget(LIVE_API.format(stream_name)) json = res_json(res) livestream = verifyjson(json, "livestream") media_id = verifyjson(livestream[0], "media_id") media_is_live = int(verifyjson(livestream[0], "media_is_live")) if not media_is_live: raise NoStreamsError(self.url) media_type = "live" if media_is_live else "video" res = urlget(PLAYER_API.format(media_type, media_id)) json = res_json(res) clip = verifyjson(json, "clip") live = verifyjson(clip, "live") bitrates = verifyjson(clip, "bitrates") streams = {} if live: for bitrate in bitrates: connection_provider = clip.get("connectionProvider", "clustering") plugins = verifyjson(json, "plugins") provider_plugin = verifyjson(plugins, connection_provider) swf = verifyjson(provider_plugin, "url") rtmp = verifyjson(provider_plugin, "netConnectionUrl") quality = self._get_quality(verifyjson(bitrate, "label")) url = verifyjson(bitrate, "url") streams[quality] = RTMPStream(self.session, { "rtmp": rtmp, "pageUrl": self.url, "playpath": url, "swfVfy": SWF_BASE + swf, "live": True }) else: for bitrate in bitrates: base_url = verifyjson(clip, "baseUrl") url = verifyjson(bitrate, "url") quality = self._get_quality(verifyjson(bitrate, "label")) streams[quality] = HTTPStream(self.session, base_url + "/" + url) 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 _get_streams(self): self.logger.debug("Fetching stream info") res = urlget(self.url, params=dict(output="json")) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") streams = {} video = verifyjson(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_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_stream(self, channel_id, quality): params = dict(channel_id=channel_id, quality=quality) res = urlopen(CHINFO_URL, data=params, headers=AJAX_HEADERS, session=self.rsession) json = res_json(res) if not json: raise NoStreamsError(self.url) elif not isinstance(json, list): raise PluginError("Invalid JSON response") info = json[0] rtmp = info.get("serverURL") playpath = info.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_vod_stream(self, movie_id): res = urlopen(VODINFO_URL.format(movie_id), headers=AJAX_HEADERS, session=self.rsession) json = res_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_streams(self): parsed = urlparse(self.url) if parsed.fragment: channelid = parsed.fragment else: channelid = parsed.path.rpartition("view/")[-1] if not channelid: raise NoStreamsError(self.url) channelid = channelid.lower().replace("/", "_") self.logger.debug("Fetching stream info") res = urlget(self.APIURL.format(channelid)) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") elif not ("success" in json and "payload" in json): raise PluginError("Invalid JSON response") elif json["success"] == False: raise NoStreamsError(self.url) streams = {} streams["live"] = HTTPStream(self.session, json["payload"]) 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_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/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, redirect=True) 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) 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 _get_hls_streams(self): url = self.HLSStreamTokenURL.format(self.channelname) try: res = urlget(url, params=dict(type="any", connection="wifi"), exception=IOError) except IOError: self.logger.debug("HLS streams not available") return {} json = res_json(res, "stream token JSON") if not isinstance(json, list): raise PluginError("Invalid JSON response") if len(json) == 0: raise PluginError("No stream token in JSON") token = verifyjson(json[0], "token") hashed = hmac.new(self.HLSStreamTokenKey, bytes(token, "utf8"), sha1) fulltoken = hashed.hexdigest() + ":" + token url = self.HLSSPlaylistURL.format(self.channelname) try: params = dict(token=fulltoken, hd="true") playlist = HLSStream.parse_variant_playlist(self.session, url, params=params) except IOError as err: raise PluginError(err) return playlist
def _get_streams(self): res = urlget(self.url) channel_id = self._find_channel_id(res.text) if not channel_id: return stream_id = self._get_stream_id(channel_id) if not stream_id: return res = urlget(STREAM_API_URL.format(stream_id), params=dict(format="all")) json = res_json(res) data = verifyjson(json, "data") items = verifyjson(data, "items") streams = {} for stream in filter(valid_stream, items): parser = STREAM_TYPES[stream["format"]] try: streams.update(parser(self.session, stream["url"])) except IOError as err: if not re.search(r"(404|400) Client Error", str(err)): self.logger.error("Failed to extract {0} streams: {1}", stream["format"].upper(), err) return streams
def find(self, channel, password=None, **extra_params): url = self.url(USHER_FIND_PATH, channel) params = dict(p=int(random() * 999999), type="any", private_code=password or "null", **extra_params) return res_json(urlget(url, params=params), "stream info JSON")
def _get_hls_streams(self): url = self.HLSStreamTokenURL.format(self.channelname) try: res = urlget(url, params=dict(type="any", connection="wifi"), exception=IOError) except IOError: self.logger.debug("HLS streams not available") return {} json = res_json(res, "stream token JSON") if not isinstance(json, list): raise PluginError("Invalid JSON response") if len(json) == 0: raise PluginError("No stream token in JSON") streams = {} token = verifyjson(json[0], "token") hashed = hmac.new(self.HLSStreamTokenKey, bytes(token, "utf8"), sha1) fulltoken = hashed.hexdigest() + ":" + token url = self.HLSSPlaylistURL.format(self.channelname) try: params = dict(token=fulltoken, hd="true") playlist = HLSStream.parse_variant_playlist(self.session, url, params=params) except IOError as err: raise PluginError(err) return playlist
def _get_stream(self, channel_id, quality): params = dict(channel_id=channel_id, quality=quality) res = urlopen(CHINFO_URL, data=params, headers=AJAX_HEADERS, session=self.rsession) json = res_json(res) if not json: raise NoStreamsError(self.url) elif not isinstance(json, list): raise PluginError("Invalid JSON response") info = json[0] rtmp = info.get("serverURL") playpath = info.get("streamName") if not (rtmp and playpath): raise NoStreamsError(self.url) parsed = urlparse(rtmp) if not parsed.scheme.startswith("rtmp"): raise NoStreamsError(self.url) if parsed.query: app = "{0}?{1}".format(parsed.path[1:], parsed.query) else: app = parsed.path[1:] return RTMPStream(self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfUrl": SWF_URL, "playpath": playpath, "app": app, "live": True })
def _get_streams_from_id(self, stream_id): res = urlget(CONFIG_URL, params=dict(id=stream_id)) config = res_json(res) media = verifyjson(config, "media") if not (media and isinstance(media, list)): return streams = {} media = media[0] hds_manifest = media.get("name") hls_manifest = media.get("hlsUrl") if hds_manifest: try: hds_streams = HDSStream.parse_manifest(self.session, hds_manifest) streams.update(hds_streams) except IOError as err: if not re.search(r"(404|400) Client Error", str(err)): self.logger.error("Failed to parse HDS manifest: {0}", err) if hls_manifest: try: hls_streams = HLSStream.parse_variant_playlist(self.session, hls_manifest, nameprefix="mobile_") streams.update(hls_streams) except IOError as err: if not re.search(r"(404|400) Client Error", str(err)): self.logger.error("Failed to parse HLS playlist: {0}", err) 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}", sel.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 = urlget(self.APIURL.format(match.group(1), time()), params=dict(output="json")) json = res_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 call(self, path, **extra_params): params = dict(as3="t", **extra_params) if self.oauth_token: params["oauth_token"] = self.oauth_token url = "{0}{1}.json".format(TWITCH_API_HOST, path) res = urlget(url, params=params, session=self.session) return res_json(res)
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 _check_channel_live(self, channelname): url = self.MetadataURL.format(channelname) res = urlget(url, params=dict(fields="mode")) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") mode = verifyjson(json, "mode") return mode == "live"
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, 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}", sel.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 = urlget(self.APIURL.format(match.group(1), time()), params=dict(output="json")) json = res_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 _get_stream_id(self, channel_id): res = urlget(CONFIG_API_URL, params=dict(id=channel_id)) config = res_json(res) media = verifyjson(config, "media") if not (media and isinstance(media, list)): return media = media[0] if not isinstance(media, dict): return return media.get("channel")
def call(self, path, format="json", host=None, **extra_params): params = dict(as3="t", **extra_params) if self.oauth_token: params["oauth_token"] = self.oauth_token url = "https://api.{0}{1}.{2}".format(host or self.host, path, format) res = urlget(url, params=params, session=self.session) if format == "json": return res_json(res) elif format == "xml": return res_xml(res) else: return res
def _get_streams(self): self.logger.debug("Fetching stream info") res = urlget(self.url, params=dict(output="json")) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") streams = {} video = verifyjson(json, "video") videos = verifyjson(video, "videoReferences") for video in videos: if not ("url" in video and "playerType" in video): continue url = video["url"] if video["playerType"] == "flash": if url.startswith("rtmp"): stream = RTMPStream( self.session, { "rtmp": url, "pageUrl": self.PageURL, "swfVfy": self.SWFURL, "live": True }) streams[str(video["bitrate"]) + "k"] = stream elif "manifest.f4m" in url: try: hdsstreams = HDSStream.parse_manifest( self.session, url) streams.update(hdsstreams) except IOError as err: self.logger.warning("Failed to get HDS manifest: {0}", err) elif video["playerType"] == "ios": try: hlsstreams = HLSStream.parse_variant_playlist( self.session, url) streams.update(hlsstreams) except IOError as err: self.logger.warning("Failed to get variant playlist: {0}", err) return streams
def _get_hls_streams(self): url = self.HLSStreamTokenURL.format(self.channelname) try: res = urlget(url, params=dict(type="iphone", connection="wifi", allow_cdn="true"), exception=IOError) except IOError: self.logger.debug("HLS streams not available") return {} json = res_json(res, "stream token JSON") if not isinstance(json, list): raise PluginError("Invalid JSON response") if len(json) == 0: raise PluginError("No stream token in JSON") token = verifyjson(json[0], "token") hashed = hmac.new(self.HLSStreamTokenKey, bytes(token, "utf8"), sha1) fulltoken = hashed.hexdigest() + ":" + token url = self.HLSSPlaylistURL.format(self.channelname) try: params = dict(token=fulltoken, hd="true", allow_cdn="true") playlist = HLSStream.parse_variant_playlist(self.session, url, params=params) except IOError as err: if "404" in err: raise PluginError(err) else: self.logger.debug("Requesting mobile transcode") payload = dict(channel=self.channelname, type="iphone") urlopen("http://usher.twitch.tv/stream/transcode_iphone.json", data=payload) return {} return playlist
def create_playlist_token(self, channel): url = self.url(HLS_TOKEN_PATH, channel) params = dict(type="iphone", allow_cdn="true") try: res = urlget(url, params=params, exception=IOError) except IOError: return None json = res_json(res, "stream token JSON") if not (isinstance(json, list) and json): raise PluginError("Invalid JSON response") token = verifyjson(json[0], "token") hashed = hmac.new(HLS_TOKEN_KEY, bytes(token, "utf8"), sha1) return "{0}:{1}".format(hashed.hexdigest(), token)
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 = urlget(self.url.rstrip("/").lower()+".json?first=true") json = res_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_hls_streams(self): url = self.HLSStreamTokenURL.format(self.channelname) try: res = urlget(url, params=dict(type="iphone", connection="wifi", allow_cdn="true"), exception=IOError) except IOError: self.logger.debug("HLS streams not available") return {} json = res_json(res, "stream token JSON") if not isinstance(json, list): raise PluginError("Invalid JSON response") if len(json) == 0: raise PluginError("No stream token in JSON") token = verifyjson(json[0], "token") hashed = hmac.new(self.HLSStreamTokenKey, bytes(token, "utf8"), sha1) fulltoken = hashed.hexdigest() + ":" + token url = self.HLSPlaylistURL.format(self.channelname) try: params = dict(token=fulltoken, hd="true", allow_cdn="true") playlist = HLSStream.parse_variant_playlist(self.session, url, nameprefix="mobile_", params=params) except IOError as err: if "404" not in str(err): raise PluginError(err) else: self.logger.debug("Requesting mobile transcode") payload = dict(channel=self.channelname, type="iphone") urlopen(self.HLSTranscodeRequest, data=payload) return {} return playlist
def _api_call(self, entrypoint, params): '''Makes a call against the api. :param entrypoint: API method to call. :param params: parameters to include in the request data. ''' url = API_URL.format(entrypoint) # default params params.update({ 'version': API_VERSION, 'locale': API_LOCALE, }) if self.session_id: params['session_id'] = self.session_id response = utils.urlget(url, params=params, session=self.session) json_response = utils.res_json(response) if json_response['error']: raise APIError(json_response['message']) return json_response
def _get_desktop_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) rtmpurl = match.group(2).replace("\\/", "/") parsed = urlparse(rtmpurl) if parsed.query: app = "{0}?{1}".format(parsed.path[1:], parsed.query) else: app = parsed.path[1:] params = { "rtmp": rtmpurl, "app": app, "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 _get_stream(self, channel_id, quality): params = dict(channel_id=channel_id, quality=quality) res = urlopen(CHINFO_URL, data=params, headers=AJAX_HEADERS, session=self.rsession) json = res_json(res) if not json: raise NoStreamsError(self.url) elif not isinstance(json, list): raise PluginError("Invalid JSON response") info = json[0] rtmp = info.get("serverURL") playpath = info.get("streamName") if not (rtmp and playpath): raise NoStreamsError(self.url) parsed = urlparse(rtmp) if not parsed.scheme.startswith("rtmp"): raise NoStreamsError(self.url) if parsed.query: app = "{0}?{1}".format(parsed.path[1:], parsed.query) else: app = parsed.path[1:] return RTMPStream( self.session, { "rtmp": rtmp, "pageUrl": self.url, "swfUrl": SWF_URL, "playpath": playpath, "app": app, "live": True })
def _get_rtmp_streams(self, channelname): self.logger.debug("Fetching stream info") res = urlget(STREAM_INFO_URL.format(channelname)) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") if not json: raise PluginError("JSON is empty") # This is ugly, not sure how to fix it. back_json_node = json["sequence"][0]["layerList"][0] if back_json_node["name"] != "background": raise PluginError("JSON data has unexpected structure") rep_node = self._get_node_by_name(back_json_node["sequenceList"], "reporting")["layerList"] main_node = self._get_node_by_name(back_json_node["sequenceList"], "main")["layerList"] if not (rep_node and main_node): raise PluginError("Error parsing stream RTMP url") swfurl = self._get_node_by_name( rep_node, "reporting")["param"]["extraParams"]["videoSwfURL"] feeds_params = self._get_node_by_name(main_node, "video")["param"] if not (swfurl and feeds_params): raise PluginError("Error parsing stream RTMP url") # Different feed qualities are available are a dict under "live" # In some cases where there's only 1 quality available, # it seems the "live" is absent. We use the single stream available # under the "customURL" key. streams = {} if "mode" in feeds_params and feeds_params["mode"] == "live": for key, quality in QUALITY_MAP.items(): url = feeds_params.get("{0}URL".format(key)) if not url: continue try: res = urlget(url, exception=IOError) except IOError: continue match = re.match(RTMP_SPLIT_REGEX, res.text) if not match: self.logger.warning("Failed to split RTMP URL: {0}", res.text) continue stream = RTMPStream( self.session, { "rtmp": match.group("host"), "app": match.group("app"), "playpath": match.group("playpath"), "swfVfy": swfurl, "live": True }) self.logger.debug("Adding URL: {0}", res.text) streams[quality] = stream return streams
def _get_rtmp_streams(self, channelname): self.logger.debug("Fetching stream info") url = self.StreamInfoURL.format(channelname) self.logger.debug("JSON data url: {0}", url) res = urlget(url) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") if len(json) == 0: raise PluginError("JSON is empty") # This is ugly, not sure how to fix it. back_json_node = json["sequence"][0]["layerList"][0] if back_json_node["name"] != "background": raise PluginError("JSON data has unexpected structure") rep_node = self._get_node_by_name(back_json_node["sequenceList"], "reporting")["layerList"] main_node = self._get_node_by_name(back_json_node["sequenceList"], "main")["layerList"] if not (rep_node and main_node): raise PluginError("Error parsing stream RTMP url") swfurl = self._get_node_by_name(rep_node, "reporting")["param"]["extraParams"]["videoSwfURL"] feeds_params = self._get_node_by_name(main_node, "video")["param"] if not (swfurl and feeds_params): raise PluginError("Error parsing stream RTMP url") streams = {} # Different feed qualities are available are a dict under "live" # In some cases where there's only 1 quality available, # it seems the "live" is absent. We use the single stream available # under the "customURL" key. if "mode" in feeds_params and feeds_params["mode"] == "live": for key, quality in self.QualityMap.items(): url_key = '{0}URL'.format(key) if url_key not in feeds_params: continue else: url = feeds_params[url_key] info = {} try: res = urlget(url, exception=IOError) except IOError: continue rtmpurl = res.text stream = RTMPStream(self.session, { "rtmp": rtmpurl, "swfVfy": swfurl, "live": True }) self.logger.debug("Adding URL: {0}", rtmpurl) streams[quality] = stream return streams
def _get_rtmp_streams(self, channelname): self.logger.debug("Fetching stream info") res = urlget(STREAM_INFO_URL.format(channelname)) json = res_json(res) if not isinstance(json, dict): raise PluginError("Invalid JSON response") if not json: raise PluginError("JSON is empty") # This is ugly, not sure how to fix it. back_json_node = json["sequence"][0]["layerList"][0] if back_json_node["name"] != "background": raise PluginError("JSON data has unexpected structure") rep_node = self._get_node_by_name(back_json_node["sequenceList"], "reporting")["layerList"] main_node = self._get_node_by_name(back_json_node["sequenceList"], "main")["layerList"] if not (rep_node and main_node): raise PluginError("Error parsing stream RTMP url") swfurl = self._get_node_by_name(rep_node, "reporting")["param"]["extraParams"]["videoSwfURL"] feeds_params = self._get_node_by_name(main_node, "video")["param"] if not (swfurl and feeds_params): raise PluginError("Error parsing stream RTMP url") # Different feed qualities are available are a dict under "live" # In some cases where there's only 1 quality available, # it seems the "live" is absent. We use the single stream available # under the "customURL" key. streams = {} if "mode" in feeds_params and feeds_params["mode"] == "live": for key, quality in QUALITY_MAP.items(): url = feeds_params.get("{0}URL".format(key)) if not url: continue try: res = urlget(url, exception=IOError) except IOError: continue match = re.match(RTMP_SPLIT_REGEX, res.text) if not match: self.logger.warning("Failed to split RTMP URL: {0}", res.text) continue stream = RTMPStream(self.session, { "rtmp": match.group("host"), "app": match.group("app"), "playpath": match.group("playpath"), "swfVfy": swfurl, "live": True }) self.logger.debug("Adding URL: {0}", res.text) streams[quality] = stream return streams