aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/livestreamer/cli.py1
-rw-r--r--src/livestreamer/compat.py21
-rw-r--r--src/livestreamer/plugins/gomtv.py179
-rw-r--r--src/livestreamer/plugins/justintv.py67
-rw-r--r--src/livestreamer/plugins/ownedtv.py37
-rw-r--r--src/livestreamer/plugins/svtplay.py21
-rw-r--r--src/livestreamer/plugins/ustreamtv.py7
-rw-r--r--src/livestreamer/plugins/youtube.py9
-rw-r--r--src/livestreamer/stream.py11
-rw-r--r--src/livestreamer/utils.py50
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("&amp;", "&")
- regexResult = urllib.unquote(regexResult)
+ match = match.replace("&amp;", "&")
+ 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 &quot;
- regexResult = regexResult.replace("&quot;", "")
+ match = match.replace("&quot;", "")
- 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)