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 _check_vod_key(self, nodeip, nodeid, userno, userip): try: conn = socket.create_connection((nodeip, self.KeyCheckPort), timeout=30) except socket.error as err: raise PluginError(("Failed to connect to key check server: {0}").format(str(err))) msg = "Login,0,{userno},{nodeid},{userip}\n".format(nodeid=nodeid, userno=userno, userip=userip) try: conn.sendall(bytes(msg, "ascii")) res = conn.recv(4096) except IOError as err: raise PluginError(("Failed to communicate with key check server: {0}").format(str(err))) if len(res) == 0: raise PluginError("Empty response from key check server") conn.close() res = str(res, "ascii").strip().split(",") return res[-1]
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.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 _check_vod_key(self, nodeip, nodeid, userno, userip): try: conn = socket.create_connection((nodeip, self.KeyCheckPort), timeout=15) except socket.error as err: raise PluginError( ("Failed to connect to key check server: {0}").format( str(err))) msg = "Login,0,{userno},{nodeid},{userip}\n".format(nodeid=nodeid, userno=userno, userip=userip) try: conn.sendall(bytes(msg, "ascii")) res = conn.recv(4096) except IOError as err: raise PluginError( ("Failed to communicate with key check server: {0}").format( str(err))) if len(res) == 0: raise PluginError("Empty response from key check server") conn.close() res = str(res, "ascii").strip().split(",") return res[-1]
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 = AMF0Packet.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 "liveHttpUrl" in result: try: hlsstreams = HLSStream.parse_variant_playlist( self.session, result["liveHttpUrl"]) streams.update(hlsstreams) except IOError as err: self.logger.warning("Failed to get variant playlist: {0}", err) if "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 "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: streams["cdn_" + name] = self._create_stream( cdn["cdnStreamUrl"], cdn["cdnStreamName"]) 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_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 = AMF0Packet.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 "liveHttpUrl" in result: try: hlsstreams = HLSStream.parse_variant_playlist(self.session, result["liveHttpUrl"]) streams.update(hlsstreams) except IOError as err: self.logger.warning("Failed to get variant playlist: {0}", err) if "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 "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: streams["cdn_" + name] = self._create_stream(cdn["cdnStreamUrl"], cdn["cdnStreamName"]) 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_channel_info(self, url): self.logger.debug("Fetching channel info") data = urlget(url, opener=urlopener) channelid = None swfurl = None match = re.search(b'flashvars.config = "livecfg/(\d+)', data) if match: channelid = int(match.group(1)) match = re.search(b"document.location.hash='/live/(\d+)'", data) if match: channelid = int(match.group(1)) match = re.search(b"xajax_load_live_config\((\d+),", data) if match: channelid = int(match.group(1)) match = re.search(b"""swfobject.embedSWF\(\n.+"(.+)", "player",""", data) if match: swfurl = str(match.group(1), "utf8") return (channelid, swfurl)
def _create_gox_params(self, flashvars, level): flashvars["adstate"] = "0" flashvars["goxkey"] = self.GOXHashKey flashvars["level"] = str(level) keys = [ "leagueid", "conid", "goxkey", "level", "uno", "uip", "adstate" ] if "playmode" in flashvars and flashvars["playmode"] == "vod": keys += ["vjoinid", "nid"] goxkey = hashlib.md5() params = {} for key in keys: if not key in flashvars: raise PluginError( ("Missing key '{0}' in flashvars").format(key)) goxkey.update(bytes(flashvars[key], "ascii")) params[key] = flashvars[key] params["goxkey"] = goxkey.hexdigest() return params
def _get_channel_name(self, url): fd = urllib.urlopen(url) data = fd.read() fd.close() match = re.search(b"live_facebook_embed_player\.swf\?channel=(\w+)", data) if match: return str(match.group(1), "ascii")
def _get_streams(self): (channelid, swfurl) = self._get_channel_info(self.url) if not (channelid and swfurl): raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") res = urlget(self.ConfigURL.format(channelid)) try: dom = xml.dom.minidom.parseString(res.text) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) streams = {} channels = dom.getElementsByTagName("channels")[0] clip = channels.getElementsByTagName("clip")[0] self.logger.debug("Verifying SWF: {0}", swfurl) swfhash, swfsize = swfverify(swfurl) for item in clip.getElementsByTagName("item"): base = item.getAttribute("base") if not base: continue if base[0] == "$": ref = re.match("\${(.+)}", base).group(1) base = self.CDN[ref] for streamel in item.getElementsByTagName("stream"): altcount = 1 name = streamel.getAttribute("label").lower().replace(" ", "_") playpath = streamel.getAttribute("name") stream = RTMPStream( self.session, { "rtmp": ("{0}/{1}").format(base, playpath), "live": True, "swfhash": swfhash, "swfsize": swfsize, "pageUrl": self.url, }, ) if not name in streams: streams[name] = stream else: if altcount == 1: streams[name + "_alt"] = stream else: streams[name + "_alt" + str(altcount)] = stream altcount += 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() 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, type): if type not in (None, StreamType.RTMP): return {} (liveid, swfurl) = self._get_channel_info(self.url) if not (liveid and swfurl): raise NoStreamsError(self.url) if not self._is_live(liveid): raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") res = urlget(self.ConfigURL.format(liveid)) try: dom = xml.dom.minidom.parseString(res.text) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) streams = {} channels = dom.getElementsByTagName("channels")[0] clip = channels.getElementsByTagName("clip")[0] items = clip.getElementsByTagName("item") for item in items: base = item.getAttribute("base") if not base: continue if base[0] == "$": ref = re.match("\${(.+)}", base).group(1) base = self.CDN[ref] for streamel in item.getElementsByTagName("stream"): name = streamel.getAttribute("label").lower().replace(" ", "_") playpath = streamel.getAttribute("name") stream = RTMPStream(self.session, { "rtmp": ("{0}/{1}").format(base, playpath), "live": True, "swfVfy": swfurl, "pageUrl": self.url }) if not name in streams: streams[name] = stream else: index = items.index(item) if index == 1: streams[name + "_alt"] = stream else: streams[name + "_alt" + str(index)] = stream return streams
def _get_streams(self): (channelid, swfurl) = self._get_channel_info(self.url) if not (channelid and swfurl): raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") res = urlget(self.ConfigURL.format(channelid)) try: dom = xml.dom.minidom.parseString(res.text) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) streams = {} channels = dom.getElementsByTagName("channels")[0] clip = channels.getElementsByTagName("clip")[0] self.logger.debug("Verifying SWF: {0}", swfurl) swfhash, swfsize = swfverify(swfurl) for item in clip.getElementsByTagName("item"): base = item.getAttribute("base") if not base: continue if base[0] == "$": ref = re.match("\${(.+)}", base).group(1) base = self.CDN[ref] for streamel in item.getElementsByTagName("stream"): altcount = 1 name = streamel.getAttribute("label").lower().replace(" ", "_") playpath = streamel.getAttribute("name") stream = RTMPStream(self.session, { "rtmp": ("{0}/{1}").format(base, playpath), "live": True, "swfhash": swfhash, "swfsize": swfsize, "pageUrl": self.url }) if not name in streams: streams[name] = stream else: if altcount == 1: streams[name + "_alt"] = stream else: streams[name + "_alt" + str(altcount)] = stream altcount += 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() 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 _create_stream_key(self, url): parsed = urlparse(url) params = parse_qsd(parsed.query) keys = ["uno", "nodeid"] for key in keys: if not key in params: raise PluginError( ("Missing key '{0}' in key check params").format(key)) userip = self._get_user_ip() nodeip = parsed.netloc try: conn = socket.create_connection( (nodeip, self.VODStreamKeyCheckPort), timeout=30) except socket.error as err: raise PluginError( ("Failed to connect to key check server: {0}").format( str(err))) msg = "Login,0,{userno},{nodeid},{userip}\n".format( nodeid=params["nodeid"], userno=params["uno"], userip=userip) try: conn.sendall(bytes(msg, "ascii")) res = conn.recv(4096) except IOError as err: raise PluginError( ("Failed to communicate with key check server: {0}").format( str(err))) if len(res) == 0: raise PluginError("Empty response from key check server") conn.close() res = str(res, "ascii").strip().split(",") return res[-1]
def _get_streams(self): (liveid, swfurl) = self._get_channel_info(self.url) if not (liveid and swfurl): raise NoStreamsError(self.url) if not self._is_live(liveid): raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") res = urlget(self.ConfigURL.format(liveid)) dom = res_xml(res, "config XML") streams = {} channels = dom.getElementsByTagName("channels")[0] clip = channels.getElementsByTagName("clip")[0] items = clip.getElementsByTagName("item") for item in items: base = item.getAttribute("base") if not base: continue if base[0] == "$": ref = re.match("\${(.+)}", base).group(1) base = self.CDN[ref] for streamel in item.getElementsByTagName("stream"): name = streamel.getAttribute("label").lower().replace(" ", "_") playpath = streamel.getAttribute("name") stream = RTMPStream(self.session, { "rtmp": ("{0}/{1}").format(base, playpath), "live": True, "swfVfy": swfurl, "pageUrl": self.url }) if not name in streams: streams[name] = stream else: index = items.index(item) if index == 1: streams[name + "_alt"] = stream else: streams[name + "_alt" + str(index)] = 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") data = urlget(self.JSONURL.format(channelid)) try: info = json.loads(str(data, "utf8")) except ValueError as err: raise PluginError(("Unable to parse JSON: {0})").format(err)) streams = {} video = verifyjson(info, "video") videos = verifyjson(video, "videoReferences") self.logger.debug("Verifying SWF: {0}", self.SWFURL) swfhash, swfsize = swfverify(self.SWFURL) for video in videos: if not ("url" in video and "playerType" in video and video["playerType"] == "flash"): continue stream = RTMPStream(self.session, { "rtmp": video["url"], "pageUrl": self.PageURL, "swfhash": swfhash, "swfsize": swfsize, "live": True }) streams[str(video["bitrate"]) + "k"] = stream return streams
def _create_stream_key(self, url): parsed = urlparse(url) params = parse_qsd(parsed.query) keys = ["uno", "nodeid"] for key in keys: if not key in params: raise PluginError(("Missing key '{0}' in key check params").format(key)) userip = self._get_user_ip() nodeip = parsed.netloc try: conn = socket.create_connection((nodeip, self.VODStreamKeyCheckPort), timeout=30) except socket.error as err: raise PluginError(("Failed to connect to key check server: {0}").format(str(err))) msg = "Login,0,{userno},{nodeid},{userip}\n".format(nodeid=params["nodeid"], userno=params["uno"], userip=userip) try: conn.sendall(bytes(msg, "ascii")) res = conn.recv(4096) except IOError as err: raise PluginError(("Failed to communicate with key check server: {0}").format(str(err))) if len(res) == 0: raise PluginError("Empty response from key check server") conn.close() res = str(res, "ascii").strip().split(",") return res[-1]
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.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 _get_stream_info(self, url): data = urlget(url) config = None match = re.search(b"'PLAYER_CONFIG': (.+)\n.+}\);", data) if match: config = match.group(1) match = re.search(b"yt.playerConfig = (.+)\;\n", data) if match: config = match.group(1) if config: try: parsed = json.loads(str(config, "utf8")) except ValueError as err: raise PluginError(("Unable to parse config JSON: {0})").format(err)) return parsed
def _get_streams(self): res = urlget(self.PlayerURL) urls = re.findall("return \"(rtmp://.+)\"", res.text) streams = {} for i, url in enumerate(urls): if i >= len(self.Streams): name = "stream_" + str(i) else: name = self.Streams[i] streams[name] = RTMPStream(self.session, { "rtmp": url, "swfUrl": self.SWFURL, "pageUrl": self.PageURL, "live": True, }) return streams
def _get_streams(self): res = urlget(self.PlayerURL) urls = re.findall("return \"(rtmp://.+)\"", res.text) streams = {} for i, url in enumerate(urls): if i >= len(self.Streams): name = "stream_" + str(i) else: name = self.Streams[i] streams[name] = RTMPStream( self.session, { "rtmp": url, "swfUrl": self.SWFURL, "pageUrl": self.PageURL, "live": True, }) 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") swfhash, swfsize = (None, None) for video in videos: if not ("url" in video and "playerType" in video): continue if video["playerType"] == "flash": if video["url"].startswith("rtmp"): if not swfhash: self.logger.debug("Verifying SWF: {0}", self.SWFURL) swfhash, swfsize = swfverify(self.SWFURL) stream = RTMPStream(self.session, { "rtmp": video["url"], "pageUrl": self.PageURL, "swfhash": swfhash, "swfsize": swfsize, "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 _create_gox_params(self, flashvars, level): flashvars["adstate"] = "0" flashvars["goxkey"] = self.GOXHashKey flashvars["level"] = str(level) keys = ["leagueid", "conid", "goxkey", "level", "uno", "uip", "adstate"] if "playmode" in flashvars and flashvars["playmode"] == "vod": keys += ["vjoinid", "nid"] goxkey = hashlib.md5() params = {} for key in keys: if not key in flashvars: raise PluginError(("Missing key '{0}' in flashvars").format(key)) goxkey.update(bytes(flashvars[key], "ascii")) params[key] = flashvars[key] params["goxkey"] = goxkey.hexdigest() return params
def get_vod_streams(self): match = re.search("flashvars\s+=\s+({.+?});", self.res.text) if not match: raise NoStreamsError(self.url) flashvars = parse_json(match.group(1), "flashvars JSON") match = re.search("var jsonData\s+= eval \((.+?)\);", self.res.text, re.DOTALL) if not match: raise NoStreamsError(self.url) playlists = parse_json(match.group(1), "playlist JSON") self.logger.info("Playlist items found:") for i, playlist in enumerate(playlists): for fvars in playlist: if self.url[-1] != "/": url = self.url + "/" else: url = self.url url = urljoin(url, "?set={1}&lang={0}".format(i, fvars["set"])) self.logger.info("[Set {1} ({0})] {2}", self.Lang[i], fvars["set"], url) params = parse_qsd(urlparse(self.url).query) currentset = int(params.get("set", "1")) lang = int(params.get("lang", "0")) flashvars.update(playlists[lang][currentset - 1]) flashvars["uip"] = self._get_user_ip() streams = {} for level, levelname in self.VODQualityLevels.items(): params = self._create_gox_params(flashvars, level) res = urlget(self.GOXVODURL, params=params, session=self.rsession) gox = GOXFile(res.text) entries = gox.filter_entries("vod") for entry in entries: streamurl = entry.ref[0] params = {} try: params["key"] = self._create_stream_key(streamurl) except PluginError as err: self.logger.warning("{0}", str(err)) continue streams[levelname] = HTTPStream(self.session, streamurl, params=params) if len(streams) == 0: self.logger.warning(("Unable to access any streams, " "make sure you have access to this VOD")) return streams
def _get_channel_name(self, url): data = urlget(url) match = re.search(b"live_facebook_embed_player\.swf\?channel=(\w+)", data) if match: return str(match.group(1), "ascii")
def get_amf_value(data, key): pattern = ("{0}\x02..(.*?)\x00").format(key) match = re.search(bytes(pattern, "ascii"), data) if match: return str(match.group(1), "ascii")
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