def _authenticate(self, username=None, password=None, cookies=None): if (username is None or password is None) and cookies is None: raise PluginError("GOMTV.net requires a username and password or a cookie") if cookies is not None: for cookie in cookies.split(";"): try: name, value = cookie.split("=") except ValueError: continue self.rsession.cookies[name.strip()] = value.strip() self.logger.info("Attempting to authenticate with cookies") else: form = dict(cmd="login", rememberme="1", mb_username=username, mb_password=password) self.logger.info("Attempting to authenticate with username and password") urlopen(self.LoginURL, data=form, headers=self.LoginHeaders, session=self.rsession) res = urlget(self.LoginCheckURL, session=self.rsession) if "Please need login" in res.text: raise PluginError("Authentication failed") if "SES_USERNICK" in self.rsession.cookies: username = self.rsession.cookies["SES_USERNICK"] self.logger.info(("Successfully logged in as {0}").format(username))
def _get_player_params(self): res = urlopen(self.url) match = re.search("<param name=\"playerKey\" value=\"(.+)\" />", res.text) if not match: raise PluginError("Missing key 'playerKey' in player params") key = match.group(1) match = re.search("<param name=\"@videoPlayer\" value=\"(\d+)\" />", res.text) if not match: raise PluginError("Missing key 'videoPlayer' in player params") video_player = match.group(1) match = re.search("<param name=\"playerID\" value=\"(\d+)\" />", res.text) if not match: raise PluginError("Missing key 'playerID' in player params") player_id = match.group(1) match = re.search("<!-- live on -->", res.text) is_live = not not match return key, video_player, player_id, is_live
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_player_params(self): res = urlopen(self.url) match = re.search("<param name=\"playerKey\" value=\"(.+)\" />", res.text) if not match: raise PluginError("Missing key 'playerKey' in player params") key = match.group(1) match = re.search("<param name=\"@videoPlayer\" value=\"(\d+)\" />", res.text) if not match: raise PluginError("Missing key 'videoPlayer' in player params") video_player = match.group(1) match = re.search("<param name=\"playerID\" value=\"(\d+)\" />", res.text) if not match: raise PluginError("Missing key 'playerID' in player params") player_id = match.group(1) match = re.search("<img src=\".+/static/images/channels/live_check.png\" />", res.text) is_live = not not match return key, video_player, player_id, is_live
def _get_player_params(self): res = urlopen(self.url) match = re.search("<param name=\"playerKey\" value=\"(.+)\" />", res.text) if not match: raise PluginError("Missing key 'playerKey' in player params") key = match.group(1) match = re.search("<param name=\"@videoPlayer\" value=\"(\d+)\" />", res.text) if not match: raise PluginError("Missing key 'videoPlayer' in player params") video_player = match.group(1) match = re.search("<param name=\"playerID\" value=\"(\d+)\" />", res.text) if not match: raise PluginError("Missing key 'playerID' in player params") player_id = match.group(1) match = re.search( "<img src=\".+/static/images/channels/live_check.png\" />", res.text) is_live = not not match return key, video_player, player_id, is_live
def get_limelight_live_streams(self): res = self._get_live_page(self.res) match = re.search('jQuery.post\("/live/ajaxGetLimelight.gom", ({.+?}),', res.text) if not match: raise NoStreamsError(self.url) ajaxparams = match.group(1) ajaxparams = dict(re.findall("(\w+):(\d+)", ajaxparams)) levels = re.findall("setFlashLevel\((\d+)\);", res.text) streams = {} for level in levels: params = ajaxparams.copy() params["level"] = level res = urlopen(self.GetLimelightStreamURL, data=params, session=self.rsession) url = unquote(res.text) if url.startswith("http"): continue try: playlist_entries = self._limelight_soap_playlist_items(url) streams.update(playlist_entries) except PluginError as err: self.logger.warning("Unable to access Limelight playlist: {0}", err) continue return streams
def _get_player_params(self): res = urlopen(self.url) match = re.search('<param name="playerKey" value="(.+)" />', res.text) if not match: raise PluginError("Missing key 'playerKey' in player params") key = match.group(1) match = re.search('<param name="@videoPlayer" value="(\d+)" />', res.text) if not match: raise PluginError("Missing key 'videoPlayer' in player params") video_player = match.group(1) match = re.search('<param name="playerID" value="(\d+)" />', res.text) if not match: raise PluginError("Missing key 'playerID' in player params") player_id = match.group(1) match = re.search('<img src="http://image.azubu.tv/static/images/channels/live_check.png" />', res.text) is_live = not not match return key, video_player, player_id, is_live
def get_alt_live_streams(self): res = self._get_live_page(self.res) match = re.search('jQuery.post\("/live/ajaxGetUrl.gom", ({.+?}),', res.text) if not match: raise NoStreamsError(self.url) ajaxparams = match.group(1) ajaxparams = dict(re.findall("(\w+):(\d+)", ajaxparams)) levels = re.findall("setFlashLevel\((\d+)\);.+?<span class=\"qtype\">(\w+)</span>", res.text) streams = {} for level, quality in levels: params = ajaxparams.copy() params["level"] = level quality = quality.lower() res = urlopen(self.GetStreamURL, data=params, session=self.rsession) url = unquote(res.text) if not urlparse(url).path.endswith(".f4m"): continue try: s = HDSStream.parse_manifest(self.session, url) if len(s) > 0: bitrate, stream = list(s.items())[0] streams[quality] = stream except IOError: self.logger.warning("Unable to parse manifest") 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 _authenticate(self, username=None, password=None, cookies=None): if (username is None or password is None) and cookies is None: raise PluginError( "GOMTV.net requires a username and password or a cookie") if cookies is not None: for cookie in cookies.split(";"): try: name, value = cookie.split("=") except ValueError: continue self.rsession.cookies[name.strip()] = value.strip() self.logger.info("Attempting to authenticate with cookies") else: form = dict(cmd="login", rememberme="1", mb_username=username, mb_password=password) self.logger.info( "Attempting to authenticate with username and password") urlopen(self.LoginURL, data=form, headers=self.LoginHeaders, session=self.rsession) res = urlget(self.LoginCheckURL, session=self.rsession) if "Please need login" in res.text: raise PluginError("Authentication failed") if "SES_USERNICK" in self.rsession.cookies: username = self.rsession.cookies["SES_USERNICK"] self.logger.info( ("Successfully logged in as {0}").format(username)) if username and password: cookie = "" for v in ("SES_MEMBERNO", "SES_STATE", "SES_MEMBERNICK", "SES_USERNICK"): if v in self.rsession.cookies: cookie += "{0}={1}; ".format(v, self.rsession.cookies[v]) self.logger.info("Cookie for reusing this session: {0}", cookie)
def _send_amf_request(self, req, key): headers = {"content-type": "application/x-amf"} res = urlopen(self.AMFGateway, data=bytes(req.serialize()), headers=headers, params=dict(playerKey=key)) return AMFPacket.deserialize(BytesIO(res.content))
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 _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 _limelight_soap_playlist_items(self, channelid): payload = """<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <tns:getPlaylistWithNItemsByChannelId xmlns:tns="http://service.data.media.pluggd.com"> <tns:in0>{0}</tns:in0> <tns:in1>0</tns:in1> <tns:in2>7</tns:in2> </tns:getPlaylistWithNItemsByChannelId> </SOAP-ENV:Body> </SOAP-ENV:Envelope>""".format(channelid) headers = { "Content-Type": "text/xml; charset=utf-8", "Referer": "http://assets.delvenetworks.com/player/loader.swf", "x-page-url": self.url } res = urlopen(self.LimelightSOAPURL, data=payload, headers=headers) dom = res_xml(res) streams = {} for item in dom.getElementsByTagName("PlaylistItem"): for stream in dom.getElementsByTagName("Stream"): for url in stream.getElementsByTagName("url"): url = get_node_text(url) break else: continue for height in stream.getElementsByTagName( "videoHeightInPixels"): height = get_node_text(height) break else: continue streamname = "{0}p".format(height) parsed = urlparse(url) if parsed.scheme.startswith("rtmp"): params = dict(rtmp=url, live=True) streams[streamname] = RTMPStream(self.session, params) return streams
def _limelight_soap_playlist_items(self, channelid): payload = """<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <tns:getPlaylistWithNItemsByChannelId xmlns:tns="http://service.data.media.pluggd.com"> <tns:in0>{0}</tns:in0> <tns:in1>0</tns:in1> <tns:in2>7</tns:in2> </tns:getPlaylistWithNItemsByChannelId> </SOAP-ENV:Body> </SOAP-ENV:Envelope>""".format(channelid) headers = { "Content-Type": "text/xml; charset=utf-8", "Referer": "http://assets.delvenetworks.com/player/loader.swf", "x-page-url": self.url } res = urlopen(self.LimelightSOAPURL, data=payload, headers=headers) dom = res_xml(res) streams = {} for item in dom.getElementsByTagName("PlaylistItem"): for stream in dom.getElementsByTagName("Stream"): for url in stream.getElementsByTagName("url"): url = get_node_text(url) break else: continue for height in stream.getElementsByTagName("videoHeightInPixels"): height = get_node_text(height) break else: continue streamname = "{0}p".format(height) parsed = urlparse(url) if parsed.scheme.startswith("rtmp"): params = dict(rtmp=url, live=True) streams[streamname] = RTMPStream(self.session, params) return streams
def _limelight_soap_playlist_items(self, channelid): payload = """<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <tns:getPlaylistWithNItemsByChannelId xmlns:tns="http://service.data.media.pluggd.com"> <tns:in0>{0}</tns:in0> <tns:in1>0</tns:in1> <tns:in2>7</tns:in2> </tns:getPlaylistWithNItemsByChannelId> </SOAP-ENV:Body> </SOAP-ENV:Envelope>""".format(channelid) headers = { "Content-Type": "text/xml; charset=utf-8", "Referer": "http://assets.delvenetworks.com/player/loader.swf", "x-page-url": self.url } res = urlopen(self.LimelightSOAPURL, data=payload, headers=headers) playlist = res_xml(res) streams = {} items = playlist.findall( ".//*{http://service.data.media.pluggd.com}playlistItems/") for item in items: streams_ = item.findall( "./{http://service.data.media.pluggd.com}streams/") for stream in streams_: url = stream.findtext( "{http://service.data.media.pluggd.com}url") height = stream.findtext( "{http://service.data.media.pluggd.com}videoHeightInPixels" ) streamname = "{0}p".format(height) parsed = urlparse(url) if parsed.scheme.startswith("rtmp"): params = dict(rtmp=url, live=True) streams[streamname] = RTMPStream(self.session, params) return streams
def _limelight_soap_playlist_items(self, channelid): payload = """<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <tns:getPlaylistWithNItemsByChannelId xmlns:tns="http://service.data.media.pluggd.com"> <tns:in0>{0}</tns:in0> <tns:in1>0</tns:in1> <tns:in2>7</tns:in2> </tns:getPlaylistWithNItemsByChannelId> </SOAP-ENV:Body> </SOAP-ENV:Envelope>""".format(channelid) headers = { "Content-Type": "text/xml; charset=utf-8", "Referer": "http://assets.delvenetworks.com/player/loader.swf", "x-page-url": self.url } res = urlopen(self.LimelightSOAPURL, data=payload, headers=headers) playlist = res_xml(res) streams = {} items = playlist.findall(".//*{http://service.data.media.pluggd.com}playlistItems/") for item in items: streams_ = item.findall("./{http://service.data.media.pluggd.com}streams/") for stream in streams_: url = stream.findtext("{http://service.data.media.pluggd.com}url") height = stream.findtext("{http://service.data.media.pluggd.com}videoHeightInPixels") streamname = "{0}p".format(height) parsed = urlparse(url) if parsed.scheme.startswith("rtmp"): params = dict(rtmp=url, live=True) streams[streamname] = RTMPStream(self.session, params) return streams
def get_alt_live_streams(self): res = self._get_live_page(self.res) match = re.search('jQuery.post\("/live/ajaxGetUrl.gom", ({.+?}),', res.text) if not match: raise NoStreamsError(self.url) ajaxparams = match.group(1) ajaxparams = dict(re.findall("(\w+):(\d+)", ajaxparams)) levels = re.findall( "setFlashLevel\((\d+)\);.+?<span class=\"qtype\">(\w+)</span>", res.text) streams = {} for level, quality in levels: params = ajaxparams.copy() params["level"] = level quality = quality.lower() res = urlopen(self.GetStreamURL, data=params, session=self.rsession) url = unquote(res.text) if not urlparse(url).path.endswith(".f4m"): continue try: s = HDSStream.parse_manifest(self.session, url) if len(s) > 0: bitrate, stream = list(s.items())[0] streams[quality] = stream except IOError: self.logger.warning("Unable to parse manifest") return streams
def get_alt_live_streams(self): res = self._get_live_page(self.res) match = re.search('jQuery.post\("/live/ajaxGetUrl.gom", ({.+?}),', res.text) if not match: raise NoStreamsError(self.url) ajaxparams = match.group(1) ajaxparams = dict(re.findall("(\w+):(\d+)", ajaxparams)) levels = re.findall("setFlashLevel\((\d+)\);", res.text) streams = {} for level in levels: params = ajaxparams.copy() params["level"] = level res = urlopen(self.GetStreamURL, data=params, session=self.rsession) url = unquote(res.text) if not urlparse(url).path.endswith(".f4m"): continue try: s = HDSStream.parse_manifest(self.session, url) streams.update(s) except IOError: self.logger.warning("Unable to parse manifest") # Hack to rename incorrect bitrate specified by GOM to something # more sane. for name, stream in streams.items(): if name == "1k": streams["1000k"] = stream del streams[name] 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_limelight_live_streams(self): res = self._get_live_page(self.res) match = re.search( 'jQuery.post\("/live/ajaxGetLimelight.gom", ({.+?}),', res.text) if not match: raise NoStreamsError(self.url) ajaxparams = match.group(1) ajaxparams = dict(re.findall("(\w+):(\d+)", ajaxparams)) levels = re.findall("setFlashLevel\((\d+)\);", res.text) streams = {} for level in levels: params = ajaxparams.copy() params["level"] = level res = urlopen(self.GetLimelightStreamURL, data=params, session=self.rsession) url = unquote(res.text) if url.startswith("http"): continue try: playlist_entries = self._limelight_soap_playlist_items(url) streams.update(playlist_entries) except PluginError as err: self.logger.warning("Unable to access Limelight playlist: {0}", err) continue 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
def _get_streams(self): self.logger.debug("Fetching stream info") headers = {"Referer": self.url} res = urlget(self.url, headers=headers) match = re.search("flashvars.*?cid[^\d]+?(\d+)", res.text) if not match: raise NoStreamsError(self.url) headers = {"Referer": self.SWFURL} form = dict(cid=match.group(1), 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 StreamError("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 StreamError(msg) elif blocktype == 11: raise StreamError("No free slots available.") if "73" in params: token = params["73"] else: raise StreamError("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 = {} if multibitrate: streams["low"] = RTMPStream( self.session, { "rtmp": "{0}/{1}".format(rtmp, playpath), "pageUrl": self.url, "swfVfy": self.SWFURL, "weeb": token, "live": True }) playpath += "HI" streams["live"] = RTMPStream( self.session, { "rtmp": "{0}/{1}".format(rtmp, playpath), "pageUrl": self.url, "swfVfy": self.SWFURL, "weeb": token, "live": True }) return streams