Bump requests version requirement to 1.0.

Fixes #67.
This commit is contained in:
Christopher Rosell 2012-12-19 20:52:53 +01:00
parent 65e7f639cf
commit 167b49fde3
14 changed files with 112 additions and 73 deletions

View File

@ -109,13 +109,13 @@ Dependencies
------------
Livestreamer and it's plugins currently depends on these software:
* ```Python``` version >= 2.6 or >= 3.0 (currently CPython and PyPy is known to work)
* ```Python``` (CPython >= 2.6 or >= 3.0 or PyPy)
* ```python-setuptools``` or ```python-distribute```
These will be installed automatically by the setup script if they are missing:
* ```python-requests``` (at least version 0.12.1)
* ```python-sh``` (*nix) or ```python-pbs``` (Windows)
* ```python-argparse``` (only needed for Python version 2.6, 3.0 and 3.1)
* ```python-requests``` (version >= 1.0)
* ```python-sh``` (*nix, version >= 1.07) or ```python-pbs``` (Windows)
* ```python-argparse``` (only needed on Python version 2.6, 3.0 and 3.1)
For RTMP based plugins:
* ```librtmp/rtmpdump``` (git clone after 2011-07-31 is needed for Twitch/JustinTV plugin)

View File

@ -6,7 +6,7 @@ from os import name as os_name
import os
version = "1.4"
deps = ["requests>=0.12.1,<0.14.2"]
deps = ["requests>=1.0,<2.0"]
packages = ["livestreamer",
"livestreamer.stream",
"livestreamer.plugins",
@ -21,7 +21,7 @@ if (version_info[0] == 2 and version_info[1] < 7) or \
if os_name == "nt":
deps.append("pbs")
else:
deps.append("sh")
deps.append("sh>=1.07,<2.0")
setup(name="livestreamer",
version=version,
@ -43,5 +43,6 @@ setup(name="livestreamer",
"Development Status :: 5 - Production/Stable",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Multimedia :: Sound/Audio",
"Topic :: Multimedia :: Video",
"Topic :: Utilities"]
)

View File

@ -1,10 +1,9 @@
from livestreamer.compat import str, bytes, urlparse
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.stream import RTMPStream
from livestreamer.utils import urlget, verifyjson
from livestreamer.utils import urlget, verifyjson, res_json
import re
import json
class DailyMotion(Plugin):
QualityMap = {
@ -28,11 +27,12 @@ class DailyMotion(Plugin):
def _check_channel_live(self, channelname):
url = self.MetadataURL.format(channelname)
res = urlget(url, params=dict(fields="mode"))
json = res_json(res)
if len(res.json) == 0:
raise PluginError("Error retrieving stream live status")
if not isinstance(json, dict):
raise PluginError("Invalid JSON response")
mode = verifyjson(res.json, "mode")
mode = verifyjson(json, "mode")
return mode == "live"
@ -67,17 +67,16 @@ class DailyMotion(Plugin):
self.logger.debug("JSON data url: {0}", url)
res = urlget(url)
json = res_json(res)
if not isinstance(res.json, dict):
raise PluginError("Stream info response is not JSON")
if not isinstance(json, dict):
raise PluginError("Invalid JSON response")
if len(res.json) == 0:
if len(json) == 0:
raise PluginError("JSON is empty")
chan_info_json = res.json
# This is ugly, not sure how to fix it.
back_json_node = chan_info_json["sequence"][0]["layerList"][0]
back_json_node = json["sequence"][0]["layerList"][0]
if back_json_node["name"] != "background":
raise PluginError("JSON data has unexpected structure")
@ -126,16 +125,22 @@ class DailyMotion(Plugin):
streams[sname] = stream
else:
res = urlget(feeds_params["customURL"])
url = feeds_params["customURL"]
if url.startswith("http"):
res = urlget(url)
rtmpurl = res.text
elif url.startswith("rtmp"):
rtmpurl = url
else:
raise PluginError("Invalid stream URL found: {0}", url)
rtmpurl = res.text
stream = RTMPStream(self.session, {
"rtmp": rtmpurl,
"swfVfy": swfurl,
"live": True
})
self.logger.debug("Adding URL: {0}", feeds_params["customURL"])
streams["live"] = stream
return streams

View File

@ -24,7 +24,7 @@ limitations under the License.
from livestreamer.compat import str, bytes, urlparse, urljoin, unquote, parse_qs
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.stream import HTTPStream
from livestreamer.utils import urlget, urlopen, parsexml, get_node_text
from livestreamer.utils import urlget, urlopen, parse_xml, get_node_text
from livestreamer.options import Options
import socket
@ -69,7 +69,7 @@ class GomTV(Plugin):
Plugin.__init__(self, url)
def _get_streams(self):
self.rsession = requests.session(prefetch=True)
self.rsession = requests.session()
options = self.options
if options.get("cookie"):
@ -265,7 +265,7 @@ class GomTV(Plugin):
return url
def _parse_gox_file(self, data):
dom = parsexml(data, "GOX XML")
dom = parse_xml(data, "GOX XML")
entries = []
for xentry in dom.getElementsByTagName("ENTRY"):

View File

@ -3,7 +3,7 @@ from livestreamer.options import Options
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.stream import RTMPStream, HLSStream
from livestreamer.utils import urlget, urlresolve, verifyjson, \
parsexml, get_node_text
res_json, res_xml, parse_xml, get_node_text
from hashlib import sha1
@ -43,7 +43,7 @@ class JustinTV(Plugin):
headers["Cookie"] = cookie
res = urlget(url, headers=headers)
dom = parsexml(res.text, "metadata XML")
dom = res_xml(res, "metadata XML")
meta = dom.getElementsByTagName("meta")[0]
metadata = {}
@ -97,8 +97,7 @@ class JustinTV(Plugin):
streams = {}
dom = parsexml(data, "config XML")
dom = parse_xml(data, "config XML")
nodes = dom.getElementsByTagName("nodes")[0]
if len(nodes.childNodes) == 0:
@ -138,17 +137,20 @@ class JustinTV(Plugin):
res = urlget(url, params=dict(type="any", connection="wifi"),
exception=IOError)
except IOError:
self.logger.debug("HLS streams not available")
return {}
if not isinstance(res.json, list):
raise PluginError("Stream info response is not JSON")
json = res_json(res, "stream token JSON")
if len(res.json) == 0:
if not isinstance(json, list):
raise PluginError("Invalid JSON response")
if len(json) == 0:
raise PluginError("No stream token in JSON")
streams = {}
token = verifyjson(res.json[0], "token")
token = verifyjson(json[0], "token")
hashed = hmac.new(self.HLSStreamTokenKey, bytes(token, "utf8"), sha1)
fulltoken = hashed.hexdigest() + ":" + token
url = self.HLSSPlaylistURL.format(self.channelname)

View File

@ -1,6 +1,6 @@
from livestreamer.stream import RTMPStream, HLSStream
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.utils import urlget
from livestreamer.utils import urlget, res_json
from time import time
import re
@ -32,13 +32,14 @@ class Livestation(Plugin):
raise PluginError(("Missing channel item-id on URL {0}").format(self.url))
res = urlget(self.APIURL.format(match.group(1), time()), params=dict(output="json"))
json = res_json(res)
if not isinstance(res.json, list):
raise PluginError("Stream info response is not JSON")
if not isinstance(json, list):
raise PluginError("Invalid JSON response")
rtmplist = {}
for jdata in res.json:
for jdata in json:
if "stream_name" not in jdata or "type" not in jdata:
continue
@ -50,7 +51,7 @@ class Livestation(Plugin):
if "token" in jdata and jdata["token"]:
playpath += jdata["token"]
if len(res.json) == 1:
if len(json) == 1:
stream_name = "live"
else:
stream_name = jdata["stream_name"]

View File

@ -1,6 +1,6 @@
from livestreamer.stream import AkamaiHDStream
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.utils import urlget, verifyjson, parsexml
from livestreamer.utils import urlget, verifyjson, res_xml, parse_json
import json
import re
@ -18,16 +18,11 @@ class Livestream(Plugin):
if match:
config = match.group(1)
try:
parsed = json.loads(config)
except ValueError as err:
raise PluginError(("Unable to parse config JSON: {0})").format(err))
return parsed
return parse_json(config, "config JSON")
def _parse_smil(self, url):
res = urlget(url)
dom = parsexml(res.text, "config XML")
dom = res_xml(res, "config XML")
httpbase = None
streams = {}

View File

@ -1,7 +1,7 @@
from livestreamer.compat import bytes, str
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.stream import RTMPStream
from livestreamer.utils import urlget, parsexml, get_node_text
from livestreamer.utils import urlget, res_xml, get_node_text
import re
@ -48,7 +48,7 @@ class OwnedTV(Plugin):
def _is_live(self, liveid):
res = urlget(self.StatusAPIURL.format(liveid))
dom = parsexml(res.text, "status XML")
dom = res_xml(res, "status XML")
live = dom.getElementsByTagName("live_is_live")
@ -69,7 +69,7 @@ class OwnedTV(Plugin):
self.logger.debug("Fetching stream info")
res = urlget(self.ConfigURL.format(liveid))
dom = parsexml(res.text, "config XML")
dom = res_xml(res, "config XML")
streams = {}
channels = dom.getElementsByTagName("channels")[0]

View File

@ -1,7 +1,7 @@
from livestreamer.compat import str
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.stream import RTMPStream, HLSStream
from livestreamer.utils import urlget, verifyjson
from livestreamer.utils import urlget, verifyjson, res_json
import re
@ -16,12 +16,13 @@ class SVTPlay(Plugin):
def _get_streams(self):
self.logger.debug("Fetching stream info")
res = urlget(self.url, params=dict(output="json"))
json = res_json(res)
if res.json is None:
raise PluginError("No JSON data in stream info")
if not isinstance(json, dict):
raise PluginError("Invalid JSON response")
streams = {}
video = verifyjson(res.json, "video")
video = verifyjson(json, "video")
videos = verifyjson(video, "videoReferences")
for video in videos:

View File

@ -1,7 +1,7 @@
from livestreamer.compat import str, bytes, parse_qs
from livestreamer.plugins import Plugin, PluginError, NoStreamsError
from livestreamer.stream import HTTPStream
from livestreamer.utils import urlget, verifyjson
from livestreamer.utils import urlget, verifyjson, parse_json
import re
import json
@ -25,12 +25,7 @@ class Youtube(Plugin):
config = match.group(1)
if config:
try:
parsed = json.loads(config)
except ValueError as err:
raise PluginError(("Unable to parse config JSON: {0})").format(err))
return parsed
return parse_json(config, "config JSON")
def _parse_stream_map(self, streammap):
streams = []

View File

@ -96,7 +96,7 @@ class AkamaiHDStreamFD(Stream):
self.logger.debug("Opening host={host} streamname={streamname}", host=self.host, streamname=self.streamname)
try:
res = urlget(url, prefetch=False, params=params)
res = urlget(url, stream=True, params=params)
except Exception as err:
raise StreamError(str(err))

View File

@ -88,7 +88,7 @@ class HLSStreamFiller(Thread):
def download_sequence(self, entry):
try:
res = urlget(entry["url"], prefetch=False,
res = urlget(entry["url"], stream=True,
exception=IOError)
except IOError as err:
self.stream.logger.error("Failed to open sequence {0}: {1}",

View File

@ -9,7 +9,7 @@ class HTTPStream(Stream):
self.args = args
def open(self):
res = urlget(self.url, prefetch=False,
res = urlget(self.url, stream=True,
exception=StreamError,
**self.args)

View File

@ -6,6 +6,7 @@ from threading import Event, Lock
import argparse
import hashlib
import hmac
import json
import os
import requests
import tempfile
@ -16,7 +17,6 @@ if is_win32:
from ctypes import windll, cast, c_ulong, c_void_p, byref
SWFKey = b"Genuine Adobe Flash Player 001"
RequestsConfig = { "danger_mode": True }
class ArgumentParser(argparse.ArgumentParser):
def convert_arg_line_to_args(self, line):
@ -202,23 +202,31 @@ class RingBuffer(Buffer):
return self.free == 0
def urlopen(url, method="get", exception=PluginError, **args):
if "data" in args and args["data"] is not None:
def urlopen(url, method="get", exception=PluginError, session=None,
timeout=20, *args, **kw):
if "data" in kw and kw["data"] is not None:
method = "post"
try:
res = requests.request(method, url, config=RequestsConfig, timeout=15, **args)
except (requests.exceptions.RequestException, IOError) as err:
raise exception(("Unable to open URL: {url} ({err})").format(url=url, err=str(err)))
if session:
res = session.request(method, url, timeout=timeout, *args, **kw)
else:
res = requests.request(method, url, timeout=timeout, *args, **kw)
res.raise_for_status()
except (requests.exceptions.RequestException, IOError) as rerr:
err = exception(("Unable to open URL: {url} ({err})").format(url=url, err=str(rerr)))
err.err = rerr
raise err
return res
def urlget(url, prefetch=True, **args):
return urlopen(url, method="get", prefetch=prefetch,
**args)
def urlget(url, stream=False, *args, **kw):
return urlopen(url, method="get", stream=stream,
*args, **kw)
def urlresolve(url):
res = urlget(url, prefetch=False, allow_redirects=False)
res = urlget(url, stream=True, allow_redirects=False)
if res.status_code == 302 and "location" in res.headers:
return res.headers["location"]
@ -251,7 +259,33 @@ def absolute_url(baseurl, url):
else:
return url
def parsexml(data, xmltype="XML", exception=PluginError):
def parse_json(data, jsontype="JSON", exception=PluginError):
try:
jsondata = json.loads(data)
except ValueError as err:
if len(res.text) > 35:
snippet = data[:35] + "..."
else:
snippet = data
raise exception(("Unable to parse {0}: {1} ({2})").format(jsontype, err, snippet))
return jsondata
def res_json(res, jsontype="JSON", exception=PluginError):
try:
jsondata = res.json()
except ValueError as err:
if len(res.text) > 35:
snippet = res.text[:35] + "..."
else:
snippet = res.text
raise exception(("Unable to parse {0}: {1} ({2})").format(jsontype, err, snippet))
return jsondata
def parse_xml(data, xmltype="XML", exception=PluginError):
try:
dom = xml.dom.minidom.parseString(data)
except Exception as err:
@ -264,6 +298,9 @@ def parsexml(data, xmltype="XML", exception=PluginError):
return dom
def res_xml(res, *args, **kw):
return parse_xml(res.text, *args, **kw)
def get_node_text(element):
res = []
for node in element.childNodes:
@ -275,7 +312,9 @@ def get_node_text(element):
else:
return "".join(res)
__all__ = ["ArgumentParser", "NamedPipe", "Buffer", "RingBuffer",
"urlopen", "urlget", "urlresolve", "swfdecompress",
"swfverify", "verifyjson", "absolute_url", "parsexml",
"swfverify", "verifyjson", "absolute_url",
"parse_json", "res_json", "parse_xml", "res_xml",
"get_node_text"]