diff options
author | Christopher Rosell <chrippa@tanuki.se> | 2012-09-07 12:39:17 +0200 |
---|---|---|
committer | Christopher Rosell <chrippa@tanuki.se> | 2012-09-07 12:39:17 +0200 |
commit | d5942525de7b54f7559402d52e89ec08fd987152 (patch) | |
tree | 13118ea3329e832d8cf59e7109a4036661c42ac7 /src | |
parent | c6ae74f76f92cd0f7875ab590c6de7669fd635a4 (diff) |
Get rid of urllib and use requests instead.
Diffstat (limited to 'src')
-rw-r--r-- | src/livestreamer/cli.py | 1 | ||||
-rw-r--r-- | src/livestreamer/compat.py | 21 | ||||
-rw-r--r-- | src/livestreamer/plugins/gomtv.py | 179 | ||||
-rw-r--r-- | src/livestreamer/plugins/justintv.py | 67 | ||||
-rw-r--r-- | src/livestreamer/plugins/ownedtv.py | 37 | ||||
-rw-r--r-- | src/livestreamer/plugins/svtplay.py | 21 | ||||
-rw-r--r-- | src/livestreamer/plugins/ustreamtv.py | 7 | ||||
-rw-r--r-- | src/livestreamer/plugins/youtube.py | 9 | ||||
-rw-r--r-- | src/livestreamer/stream.py | 11 | ||||
-rw-r--r-- | src/livestreamer/utils.py | 50 |
10 files changed, 192 insertions, 211 deletions
diff --git a/src/livestreamer/cli.py b/src/livestreamer/cli.py index f294d35..24b5c85 100644 --- a/src/livestreamer/cli.py +++ b/src/livestreamer/cli.py @@ -116,7 +116,6 @@ def write_stream(fd, out, progress): sys.stderr.write("\n") logger.info("Stream ended") - fd.close() if out != stdout: out.close() diff --git a/src/livestreamer/compat.py b/src/livestreamer/compat.py index fb69c6c..d91021f 100644 --- a/src/livestreamer/compat.py +++ b/src/livestreamer/compat.py @@ -1,4 +1,5 @@ -import sys, os +import os +import sys is_py2 = (sys.version_info[0] == 2) is_py3 = (sys.version_info[0] == 3) @@ -20,17 +21,11 @@ elif is_py3: str = str try: - import urllib.request as urllib + from urllib.parse import urlparse, urljoin, quote, unquote, parse_qs except ImportError: - import urllib2 as urllib + from urlparse import urlparse, urljoin, parse_qs + from urllib import quote, unquote -try: - from urllib.parse import urlparse, parse_qs, urlencode - import http.cookiejar as cookiejar -except ImportError: - from urlparse import urlparse, parse_qs - from urllib import urlencode - import cookielib as cookiejar - -__all__ = ["is_py2", "is_py3", "is_win32", "input", "stdout", "str", - "bytes", "urllib", "urlparse", "parse_qs", "cookiejar"] +__all__ = ["is_py2", "is_py3", "is_win32", "input", "stdout", + "str", "bytes", "urlparse", "urljoin", "parse_qs", + "quote", "unquote"] diff --git a/src/livestreamer/plugins/gomtv.py b/src/livestreamer/plugins/gomtv.py index 8d7c3c9..5a44227 100644 --- a/src/livestreamer/plugins/gomtv.py +++ b/src/livestreamer/plugins/gomtv.py @@ -21,13 +21,15 @@ limitations under the License. """ -from livestreamer.compat import str, bytes, urlencode, urllib, urlparse, cookiejar +from livestreamer.compat import str, bytes, urlparse, urljoin, unquote from livestreamer.plugins import Plugin, PluginError, NoStreamsError from livestreamer.stream import HTTPStream -from livestreamer.utils import urlget +from livestreamer.utils import urlget, urlopen from livestreamer.options import Options -import xml.dom.minidom, re +import re +import requests +import xml.dom.minidom class GomTV(Plugin): BaseURL = "http://www.gomtv.net" @@ -35,6 +37,15 @@ class GomTV(Plugin): LoginURL = "https://ssl.gomtv.net/userinfo/loginProcess.gom" LoginCheckURL = BaseURL + "/forum/list.gom?m=my" + LoginHeaders = { + "Referer": BaseURL + } + + StreamHeaders = { + "User-Agent": "KPeerClient" + } + + options = Options({ "cookie": None, "username": None, @@ -55,39 +66,37 @@ class GomTV(Plugin): Plugin.__init__(self, url) def _get_streams(self): - options = self.options - # Setting urllib up so that we can store cookies - self.cookiejar = cookiejar.CookieJar() - self.opener = urllib.build_opener(urllib.HTTPCookieProcessor(self.cookiejar)) + self.rsession = requests.session(prefetch=True) + options = self.options if options.get("cookie"): - self.authenticate(cookies=options.get("cookie")) + self._authenticate(cookies=options.get("cookie")) else: - self.authenticate(options.get("username"), options.get("password")) + self._authenticate(options.get("username"), options.get("password")) streams = {} qualities = ["HQ", "SQ", "HQTest", "SQTest"] - response = self.grabLivePage(self.url) + res = self._get_live_page(self.url) + urls = self._find_stream_urls(res.text) for quality in qualities: - urls = self.parseHTML(response, quality) - for url in urls: # Grab the response of the URL listed on the Live page for a stream - goxFile = urlget(url, opener=self.opener) + url = url.format(quality=quality) + res = urlget(url, session=self.rsession) # The response for the GOX XML if an incorrect stream quality is chosen is 1002. - if goxFile != b"1002" and len(goxFile) > 0: - streamUrl = self.parseStreamURL(goxFile) - req = urllib.Request(streamUrl, headers={"User-Agent": "KPeerClient"}) - streams[quality] = HTTPStream(self.session, req) + if res.text != "1002" and len(res.text) > 0: + streamurl = self._parse_gox_file(res.text) + streams[quality] = HTTPStream(self.session, streamurl, + headers=self.StreamHeaders) return streams - def authenticate(self, username=None, password=None, cookies=None): + 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 cookie") + raise PluginError("GOMTV.net requires a username and password or a cookie") if cookies is not None: for cookie in cookies.split(";"): @@ -96,49 +105,38 @@ class GomTV(Plugin): except ValueError: continue - c = cookiejar.Cookie(version=0, name=name.strip(), value=value.strip(), - port=None, port_specified=False, domain="gomtv.net", - domain_specified=False, domain_initial_dot=False, path="/", - path_specified=True, secure=False, expires=None, discard=True, - comment=None, comment_url=None, rest={"HttpOnly": None}, - rfc2109=False) - self.cookiejar.set_cookie(c) + self.rsession.cookies[name.strip()] = value.strip() self.logger.info("Attempting to authenticate with cookies") else: - values = { - "cmd": "login", - "rememberme": "1", - "mb_username": username, - "mb_password": password - } - data = bytes(urlencode(values), "ascii") - headers = {"Referer": self.BaseURL} - request = urllib.Request(self.LoginURL, data, headers) - - self.logger.info("Attempting to authenticate with username/password") - urlget(request, opener=self.opener) - - - req = urllib.Request(self.LoginCheckURL) - if b"Please need login" in urlget(req, opener=self.opener): + 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") - for cookie in self.cookiejar: - if cookie.name == "SES_USERNICK": - self.logger.info(("Successfully logged in as {0}").format(cookie.value)) - break + if "SES_USERNICK" in self.rsession.cookies: + username = self.rsession.cookies["SES_USERNICK"] + self.logger.info(("Successfully logged in as {0}").format(username)) - def getEventLivePageURL(self, gomtvLiveURL, response): - match = re.search(' \"(.*)\";', response) + def _get_event_url(self, prefix, data): + match = re.search(' \"(.*)\";', data) if not match: - raise PluginError("Event Live Page URL not found") + raise PluginError("Event live page URL not found") - return urljoin(gomtvLiveURL, match.group(1)) + return urljoin(prefix, match.group(1)) - def grabLivePage(self, gomtvLiveURL): - response = urlget(gomtvLiveURL, opener=self.opener) + def _get_live_page(self, url): + res = urlget(url, session=self.rsession) # If a special event occurs, we know that the live page response # will just be some JavaScript that redirects the browser to the @@ -146,77 +144,76 @@ class GomTV(Plugin): # is less than 200 characters long, and that real live pages are # more than that. - if len(response) < 200: + if len(res.text) < 200: # Grabbing the real live page URL - gomtvLiveURL = self.getEventLivePageURL(gomtvLiveURL, response) - response = urlget(gomtvLiveURL, opener=self.opener) + url = self._parse_event_url(url, res.text) + res = urlget(url, session=self.rsession) - return response + return res - def parseHTML(self, response, quality): - urlFromHTML = None + def _find_stream_urls(self, data): + url = None # Parsing through the live page for a link to the gox XML file. # Quality is simply passed as a URL parameter e.g. HQ, SQ, SQTest try: - patternHTML = b"[^/]+var.+(http://www.gomtv.net/gox[^;]+;)" - urlFromHTML = re.search(patternHTML, response).group(1) - urlFromHTML = re.sub(b'\" \+ playType \+ \"', bytes(quality, "utf8"), urlFromHTML) + patternhtml = "[^/]+var.+(http://www.gomtv.net/gox[^;]+;)" + url = re.search(patternhtml, data).group(1) + url = re.sub('\" \+ playType \+ \"', "{quality}", url) except AttributeError: - raise PluginError("Unable to find the majority of the GOMtv XML URL on the Live page.") + raise PluginError("Unable to find the majority of the GOMTV.net XML URL on the Live page") # Finding the title of the stream, probably not necessary but # done for completeness try: - patternTitle = b"this\.title[^;]+;" - titleFromHTML = re.search(patternTitle, response).group(0) - titleFromHTML = re.search(b'\"(.*)\"', titleFromHTML).group(0) - titleFromHTML = re.sub(b'"', b"", titleFromHTML) - urlFromHTML = re.sub(b'"\+ tmpThis.title[^;]+;', titleFromHTML, urlFromHTML) + patterntitle = "this\.title[^;]+;" + title = re.search(patterntitle, data).group(0) + title = re.search('\"(.*)\"', title).group(0) + title = re.sub('"', "", title) + url = re.sub('"\+ tmpThis.title[^;]+;', title, url) except AttributeError: - raise PluginError("Unable to find the stream title on the Live page.") + raise PluginError("Unable to find the stream title on the Live page") # Check for multiple streams going at the same time, and extract the conid and the title # Those streams have the class "live_now" - patternLive = b'<a\shref=\"/live/index.gom\?conid=(?P<conid>\d+)\"\sclass=\"live_now\"\stitle=\"(?P<title>[^\"]+)' - live_streams = re.findall(patternLive, response) + patternlive = '<a\shref=\"/live/index.gom\?conid=(?P<conid>\d+)\"\sclass=\"live_now\"\stitle=\"(?P<title>[^\"]+)' + streams = re.findall(patternlive, data) - if len(live_streams) > 1: - liveUrls = [] - for stream in live_streams: + if len(streams) > 1: + urls = [] + for stream in streams: # Modify the urlFromHTML according to the user - singleUrlFromHTML = re.sub(b"conid=\d+", b"conid=" + stream[0], urlFromHTML) - singleTitleHTML = b"+".join(stream[0].split(b" ")) - singleUrlFromHTML = re.sub(b"title=[\w|.|+]*", b"title=" + singleTitleHTML, singleUrlFromHTML) - liveUrls.append(str(singleUrlFromHTML, "utf8")) + singleurl = re.sub("conid=\d+", "conid=" + stream[0], url) + singletitlehtml = "+".join(stream[0].split(" ")) + singleurl = re.sub("title=[\w|.|+]*", "title=" + singletitlehtml, singleurl) + urls.append(singleurl) - return liveUrls + return urls else: - if urlFromHTML is None: + if url is None: return [] else: - return [str(urlFromHTML, "utf8")] + return [url] - def parseStreamURL(self, response): + def _parse_gox_file(self, data): # Grabbing the gomcmd URL try: - streamPattern = b'<REF href="([^"]*)"\s*/>' - regexResult = re.search(streamPattern, response).group(1) + patternstream = '<REF href="([^"]*)"\s*/>' + match = re.search(patternstream, data).group(1) except AttributeError: - raise PluginError("Unable to find the gomcmd URL in the GOX XML file.") + raise PluginError("Unable to find the gomcmd URL in the GOX XML file") - regexResult = str(regexResult, "utf8") - regexResult = regexResult.replace("&", "&") - regexResult = urllib.unquote(regexResult) + match = match.replace("&", "&") + match = unquote(match) # SQ and SQTest streams can be gomp2p links, with actual stream address passed as a parameter. - if regexResult.startswith("gomp2p://"): - regexResult, n = re.subn("^.*LiveAddr=", "", regexResult) + if match.startswith("gomp2p://"): + match, n = re.subn("^.*LiveAddr=", "", match) # Cosmetics, getting rid of the HTML entity, we don't # need either of the " character or " - regexResult = regexResult.replace(""", "") + match = match.replace(""", "") - return regexResult + return match __plugin__ = GomTV diff --git a/src/livestreamer/plugins/justintv.py b/src/livestreamer/plugins/justintv.py index 0f7ec42..4fe93be 100644 --- a/src/livestreamer/plugins/justintv.py +++ b/src/livestreamer/plugins/justintv.py @@ -1,17 +1,19 @@ +from livestreamer.compat import str +from livestreamer.options import Options from livestreamer.plugins import Plugin, PluginError, NoStreamsError from livestreamer.stream import RTMPStream from livestreamer.utils import swfverify, urlget -from livestreamer.compat import urllib, str -from livestreamer.options import Options -import xml.dom.minidom, re, sys, random +import re +import random +import xml.dom.minidom class JustinTV(Plugin): options = Options({ "cookie": None }) - StreamInfoURL = "http://usher.justin.tv/find/{0}.xml?type=any&p={1}&b_id=true&private_code=null&group=&channel_subscription={2}" + StreamInfoURL = "http://usher.justin.tv/find/{0}.xml" MetadataURL = "http://www.justin.tv/meta/{0}.xml?on_site=true" SWFURL = "http://www.justin.tv/widgets/live_embed_player.swf" @@ -20,21 +22,21 @@ class JustinTV(Plugin): return ("justin.tv" in url) or ("twitch.tv" in url) def _get_channel_name(self, url): - return url.rstrip("/").rpartition("/")[2] + return url.rstrip("/").rpartition("/")[2].lower() + + def _get_metadata(self): + url = self.MetadataURL.format(self.channelname) - def _get_metadata(self, channel): + headers = {} cookie = self.options.get("cookie") if cookie: - headers = {"Cookie": cookie} - req = urllib.Request(self.MetadataURL.format(channel), headers=headers) - else: - req = urllib.Request(self.MetadataURL.format(channel)) + headers["Cookie"] = cookie - data = urlget(req) + res = urlget(url, headers=headers) try: - dom = xml.dom.minidom.parseString(data) + dom = xml.dom.minidom.parseString(res.text) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) @@ -63,34 +65,41 @@ class JustinTV(Plugin): else: return "".join(res) - def _get_streaminfo(self, channelname): - def clean_tag(tag): - if tag[0] == "_": - return tag[1:] - else: - return tag - - + def _authenticate(self): chansub = None + if self.options.get("cookie") is not None: self.logger.info("Attempting to authenticate using cookies") - metadata = self._get_metadata(channelname) + metadata = self._get_metadata() chansub = metadata["access_guid"] if "login" in metadata and metadata["login"] is not None: self.logger.info("Successfully logged in as {0}", metadata["login"]) + return chansub + + def _get_streaminfo(self): + def clean_tag(tag): + if tag[0] == "_": + return tag[1:] + else: + return tag + + chansub = self._authenticate() - randomp = int(random.random() * 999999) - url = self.StreamInfoURL.format(channelname.lower(), randomp, chansub) + 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") - data = urlget(url) + res = urlget(url, params=params) + data = res.text # fix invalid xml - data = re.sub(b"<(\d+)", b"<_\g<1>", data) - data = re.sub(b"</(\d+)", b"</_\g<1>", data) + data = re.sub("<(\d+)", "<_\g<1>", data) + data = re.sub("</(\d+)", "</_\g<1>", data) streams = {} @@ -132,12 +141,12 @@ class JustinTV(Plugin): return streams def _get_streams(self): - channelname = self._get_channel_name(self.url) + self.channelname = self._get_channel_name(self.url) - if not channelname: + if not self.channelname: raise NoStreamsError(self.url) - return self._get_streaminfo(channelname) + return self._get_streaminfo() __plugin__ = JustinTV diff --git a/src/livestreamer/plugins/ownedtv.py b/src/livestreamer/plugins/ownedtv.py index 1d2fac4..c7b3b74 100644 --- a/src/livestreamer/plugins/ownedtv.py +++ b/src/livestreamer/plugins/ownedtv.py @@ -1,24 +1,10 @@ -from livestreamer.compat import urllib, bytes, str +from livestreamer.compat import bytes, str from livestreamer.plugins import Plugin, PluginError, NoStreamsError from livestreamer.stream import RTMPStream from livestreamer.utils import urlget, swfverify -import xml.dom.minidom, re - -class RelativeRedirectHandler(urllib.HTTPRedirectHandler): - def http_error_302(self, req, fp, code, msg, headers): - if "location" in headers and headers["location"][0] == "/": - absurl = ("{scheme}://{host}{path}").format( - scheme=req.get_type(), host=req.get_host(), - path=headers["location"]) - del headers["location"] - headers["location"] = absurl - - return urllib.HTTPRedirectHandler.http_error_301( - self, req, fp, code, msg, headers) - -urlopener = urllib.build_opener(RelativeRedirectHandler) - +import re +import xml.dom.minidom class OwnedTV(Plugin): ConfigURL = "http://www.own3d.tv/livecfg/{0}" @@ -35,26 +21,27 @@ class OwnedTV(Plugin): def _get_channel_info(self, url): self.logger.debug("Fetching channel info") - data = urlget(url, opener=urlopener) + res = urlget(url) + data = res.text channelid = None swfurl = None - match = re.search(b'flashvars.config = "livecfg/(\d+)', data) + match = re.search('flashvars.config = "livecfg/(\d+)', data) if match: channelid = int(match.group(1)) - match = re.search(b"document.location.hash='/live/(\d+)'", data) + match = re.search("document.location.hash='/live/(\d+)'", data) if match: channelid = int(match.group(1)) - match = re.search(b"xajax_load_live_config\((\d+),", data) + match = re.search("xajax_load_live_config\((\d+),", data) if match: channelid = int(match.group(1)) - match = re.search(b"""swfobject.embedSWF\(\n.+"(.+)", "player",""", data) + match = re.search("""swfobject.embedSWF\(\n.+"(.+)", "player",""", data) if match: - swfurl = str(match.group(1), "utf8") + swfurl = match.group(1) return (channelid, swfurl) @@ -65,10 +52,10 @@ class OwnedTV(Plugin): raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") - data = urlget(self.ConfigURL.format(channelid)) + res = urlget(self.ConfigURL.format(channelid)) try: - dom = xml.dom.minidom.parseString(data) + dom = xml.dom.minidom.parseString(res.text) except Exception as err: raise PluginError(("Unable to parse config XML: {0})").format(err)) diff --git a/src/livestreamer/plugins/svtplay.py b/src/livestreamer/plugins/svtplay.py index 3ca85a9..a3bfa5a 100644 --- a/src/livestreamer/plugins/svtplay.py +++ b/src/livestreamer/plugins/svtplay.py @@ -3,11 +3,10 @@ from livestreamer.plugins import Plugin, PluginError, NoStreamsError from livestreamer.stream import RTMPStream from livestreamer.utils import urlget, swfverify, verifyjson -import json, re - +import re class SVTPlay(Plugin): - JSONURL = "http://svtplay.se/live/{0}?output=json" + JSONURL = "http://svtplay.se/live/{0}" SWFURL = "http://www.svtplay.se/public/swf/video/svtplayer-2012.15.swf" PageURL = "http://www.svtplay.se" @@ -18,9 +17,10 @@ class SVTPlay(Plugin): def _get_channel_id(self, url): self.logger.debug("Fetching channel id") - data = urlget(url) + res = urlget(url) + - match = re.search(b'data-json-href="/live/(\d+)"', data) + match = re.search('data-json-href="/live/(\d+)"', res.text) if match: return int(match.group(1)) @@ -31,15 +31,13 @@ class SVTPlay(Plugin): raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") - data = urlget(self.JSONURL.format(channelid)) + res = urlget(self.JSONURL.format(channelid), params=dict(output="json")) - try: - info = json.loads(str(data, "utf8")) - except ValueError as err: - raise PluginError(("Unable to parse JSON: {0})").format(err)) + if res.json is None: + raise PluginError("No JSON data in stream info") streams = {} - video = verifyjson(info, "video") + video = verifyjson(res.json, "video") videos = verifyjson(video, "videoReferences") self.logger.debug("Verifying SWF: {0}", self.SWFURL) @@ -58,7 +56,6 @@ class SVTPlay(Plugin): }) streams[str(video["bitrate"]) + "k"] = stream - return streams __plugin__ = SVTPlay diff --git a/src/livestreamer/plugins/ustreamtv.py b/src/livestreamer/plugins/ustreamtv.py index 4f0d3fd..e339fe0 100644 --- a/src/livestreamer/plugins/ustreamtv.py +++ b/src/livestreamer/plugins/ustreamtv.py @@ -14,9 +14,9 @@ class UStreamTV(Plugin): return "ustream.tv" in url def _get_channel_id(self, url): - data = urlget(url) + res = urlget(url) - match = re.search(b"channelId=(\d+)", data) + match = re.search("channelId=(\d+)", res.text) if match: return int(match.group(1)) @@ -34,7 +34,8 @@ class UStreamTV(Plugin): raise NoStreamsError(self.url) self.logger.debug("Fetching stream info") - data = urlget(self.AMFURL.format(channelid)) + res = urlget(self.AMFURL.format(channelid)) + data = res.content playpath = get_amf_value(data, "streamName") cdnurl = get_amf_value(data, "cdnUrl") diff --git a/src/livestreamer/plugins/youtube.py b/src/livestreamer/plugins/youtube.py index 606a57e..c5f5d37 100644 --- a/src/livestreamer/plugins/youtube.py +++ b/src/livestreamer/plugins/youtube.py @@ -12,20 +12,21 @@ class Youtube(Plugin): return "youtube.com" in url def _get_stream_info(self, url): - data = urlget(url) + res = urlget(url) + data = res.text config = None - match = re.search(b"'PLAYER_CONFIG': (.+)\n.+}\);", data) + match = re.search("'PLAYER_CONFIG': (.+)\n.+}\);", data) if match: config = match.group(1) - match = re.search(b"yt.playerConfig = (.+)\;\n", data) + match = re.search("yt.playerConfig = (.+)\;\n", data) if match: config = match.group(1) if config: try: - parsed = json.loads(str(config, "utf8")) + parsed = json.loads(config) except ValueError as err: raise PluginError(("Unable to parse config JSON: {0})").format(err)) diff --git a/src/livestreamer/stream.py b/src/livestreamer/stream.py index e71e3bc..f72217b 100644 --- a/src/livestreamer/stream.py +++ b/src/livestreamer/stream.py @@ -1,5 +1,5 @@ -from .utils import urlopen from .compat import str, is_win32 +from .utils import urlopen import os import pbs @@ -89,13 +89,18 @@ class RTMPStream(StreamProcess): return False class HTTPStream(Stream): - def __init__(self, session, url): + def __init__(self, session, url, **args): Stream.__init__(self, session) self.url = url + self.args = args def open(self): - return urlopen(self.url) + try: + res = urlopen(self.url, prefetch=False, **self.args) + except Exception as err: + raise StreamError(str(err)) + return res.raw __all__ = ["StreamError", "Stream", "StreamProcess", "RTMPStream", "HTTPStream"] diff --git a/src/livestreamer/utils.py b/src/livestreamer/utils.py index 75e7213..90b9379 100644 --- a/src/livestreamer/utils.py +++ b/src/livestreamer/utils.py @@ -1,9 +1,13 @@ -from .compat import urllib from .plugins import PluginError -import hmac, hashlib, zlib, argparse +import argparse +import hashlib +import hmac +import requests +import zlib -SWF_KEY = b"Genuine Adobe Flash Player 001" +SWFKey = b"Genuine Adobe Flash Player 001" +RequestsConfig = { "danger_mode": True } class ArgumentParser(argparse.ArgumentParser): def convert_arg_line_to_args(self, line): @@ -18,42 +22,28 @@ class ArgumentParser(argparse.ArgumentParser): else: yield "--%s" % line -def urlopen(url, data=None, timeout=None, opener=None): - try: - if opener is not None: - fd = opener.open(url, data, timeout) - else: - fd = urllib.urlopen(url, data, timeout) - - except IOError as err: - if type(err) is urllib.URLError: - raise PluginError(err.reason) - else: - raise PluginError(err) - - return fd - -def urlget(url, data=None, timeout=15, opener=None): - fd = urlopen(url, data, timeout, opener) +def urlopen(url, method="get", **args): + if "data" in args and args["data"] is not None: + method = "post" try: - data = fd.read() - fd.close() - except IOError as err: - if type(err) is urllib.URLError: - raise PluginError(err.reason) - else: - raise PluginError(err) + res = requests.request(method, url, config=RequestsConfig, timeout=15, **args) + except requests.exceptions.RequestException as err: + raise PluginError(("Unable to open URL: {url} ({err})").format(url=url, err=str(err))) + + return res - return data +def urlget(url, **args): + return urlopen(url, method="get", **args) def swfverify(url): - swf = urlget(url) + res = urlopen(url) + swf = res.content if swf[:3] == b"CWS": swf = b"F" + swf[1:8] + zlib.decompress(swf[8:]) - h = hmac.new(SWF_KEY, swf, hashlib.sha256) + h = hmac.new(SWFKey, swf, hashlib.sha256) return h.hexdigest(), len(swf) |