From 22dd8a40c8bb9c1c7e845a47df5b84dfe2a8527e Mon Sep 17 00:00:00 2001 From: Christopher Rosell Date: Sat, 21 Apr 2012 21:57:20 +0200 Subject: Added PluginError exception to make error handling nicer. Also added a utility function to do HTTP requests. --- src/livestreamer/__init__.py | 3 +++ src/livestreamer/cli.py | 5 ++++- src/livestreamer/plugins/__init__.py | 3 +++ src/livestreamer/plugins/justintv.py | 33 ++++++++++++++++++--------------- src/livestreamer/plugins/ownedtv.py | 19 ++++++++++--------- src/livestreamer/plugins/ustreamtv.py | 13 +++++-------- src/livestreamer/utils.py | 24 +++++++++++++++++++++--- 7 files changed, 64 insertions(+), 36 deletions(-) diff --git a/src/livestreamer/__init__.py b/src/livestreamer/__init__.py index 6a0b463..9eb2264 100644 --- a/src/livestreamer/__init__.py +++ b/src/livestreamer/__init__.py @@ -16,4 +16,7 @@ def resolve_url(url): def get_plugins(): return plugins.get_plugins() + +PluginError = plugins.PluginError + plugins.load_plugins(plugins) diff --git a/src/livestreamer/cli.py b/src/livestreamer/cli.py index 179b80d..6d7aac5 100644 --- a/src/livestreamer/cli.py +++ b/src/livestreamer/cli.py @@ -24,7 +24,10 @@ def handle_url(args): if not channel: exit(("No plugin can handle url: {0}").format(args.url)) - streams = channel.get_streams() + try: + streams = channel.get_streams() + except livestreamer.PluginError as err: + exit(("Error from plugin while retrieving streams: {0}").format(err)) if len(streams) == 0: exit(("No streams found on url: {0}").format(args.url)) diff --git a/src/livestreamer/plugins/__init__.py b/src/livestreamer/plugins/__init__.py index fe62179..d24b4de 100644 --- a/src/livestreamer/plugins/__init__.py +++ b/src/livestreamer/plugins/__init__.py @@ -35,6 +35,9 @@ class Plugin(object): def _get_streams(self): raise NotImplementedError +class PluginError(Exception): + pass + def load_plugins(plugins): for loader, name, ispkg in pkgutil.iter_modules(plugins.__path__): file, pathname, desc = imp.find_module(name, plugins.__path__) diff --git a/src/livestreamer/plugins/justintv.py b/src/livestreamer/plugins/justintv.py index f8e4e0b..620db63 100644 --- a/src/livestreamer/plugins/justintv.py +++ b/src/livestreamer/plugins/justintv.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -from livestreamer.plugins import Plugin, register_plugin +from livestreamer.plugins import Plugin, PluginError, register_plugin from livestreamer.stream import RTMPStream -from livestreamer.utils import swfverify +from livestreamer.utils import swfverify, urlget from livestreamer.compat import urllib, str import xml.dom.minidom, re, sys, random @@ -28,11 +28,9 @@ class JustinTV(Plugin): self.cookie = args.jtv_cookie def _get_channel_name(self, url): - fd = urllib.urlopen(url) - data = fd.read() - fd.close() - + data = urlget(url) match = re.search(b"live_facebook_embed_player\.swf\?channel=(\w+)", data) + if match: return str(match.group(1), "ascii") @@ -43,12 +41,13 @@ class JustinTV(Plugin): else: req = urllib.Request(self.MetadataURL.format(channel)) + data = urlget(req) - fd = urllib.urlopen(req) - data = fd.read() - fd.close() + try: + dom = xml.dom.minidom.parseString(data) + except Exception as err: + raise PluginError(("Unable to parse config XML: {0})").format(err)) - dom = xml.dom.minidom.parseString(data) meta = dom.getElementsByTagName("meta")[0] metadata = {} @@ -80,19 +79,23 @@ class JustinTV(Plugin): randomp = int(random.random() * 999999) if "chansub_guid" in metadata: - fd = urllib.urlopen(self.StreamInfoURLSub.format(channelname, randomp, metadata["chansub_guid"])) + url = self.StreamInfoURLSub.format(channelname, randomp, metadata["chansub_guid"]) else: - fd = urllib.urlopen(self.StreamInfoURL.format(channelname, randomp)) + url = self.StreamInfoURL.format(channelname, randomp) - data = fd.read() - fd.close() + data = urlget(url) # fix invalid xml data = re.sub(b"<(\d+)", b"<_\g<1>", data) data = re.sub(b"", data) streams = {} - dom = xml.dom.minidom.parseString(data) + + try: + dom = xml.dom.minidom.parseString(data) + except Exception as err: + raise PluginError(("Unable to parse config XML: {0})").format(err)) + nodes = dom.getElementsByTagName("nodes")[0] swfhash, swfsize = swfverify(self.SWFURL) diff --git a/src/livestreamer/plugins/ownedtv.py b/src/livestreamer/plugins/ownedtv.py index 6461c15..c15e8c4 100644 --- a/src/livestreamer/plugins/ownedtv.py +++ b/src/livestreamer/plugins/ownedtv.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 -from livestreamer.plugins import Plugin, register_plugin -from livestreamer.stream import RTMPStream from livestreamer.compat import urllib +from livestreamer.plugins import Plugin, PluginError, register_plugin +from livestreamer.stream import RTMPStream +from livestreamer.utils import urlget import xml.dom.minidom, re @@ -34,9 +35,7 @@ class OwnedTV(Plugin): return "own3d.tv" in url def _get_channel_id(self, url): - fd = urlopener.open(url) - data = fd.read() - fd.close() + data = urlget(url, opener=urlopener) match = re.search(b"document.location.hash='/live/(\d+)'", data) if match: @@ -51,11 +50,13 @@ class OwnedTV(Plugin): streams = {} if channelid: - fd = urllib.urlopen(self.ConfigURL.format(channelid)) - data = fd.read() - fd.close() + data = urlget(self.ConfigURL.format(channelid)) + + try: + dom = xml.dom.minidom.parseString(data) + except Exception as err: + raise PluginError(("Unable to parse config XML: {0})").format(err)) - dom = xml.dom.minidom.parseString(data) channels = dom.getElementsByTagName("channels")[0] clip = channels.getElementsByTagName("clip")[0] diff --git a/src/livestreamer/plugins/ustreamtv.py b/src/livestreamer/plugins/ustreamtv.py index 63a3134..063923e 100644 --- a/src/livestreamer/plugins/ustreamtv.py +++ b/src/livestreamer/plugins/ustreamtv.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 -from livestreamer.plugins import Plugin, register_plugin +from livestreamer.compat import str, bytes +from livestreamer.plugins import Plugin, PluginError, register_plugin from livestreamer.stream import RTMPStream -from livestreamer.compat import urllib, str, bytes +from livestreamer.utils import urlget import xml.dom.minidom, re @@ -15,9 +16,7 @@ class UStreamTV(Plugin): return "ustream.tv" in url def _get_channel_id(self, url): - fd = urllib.urlopen(url) - data = fd.read() - fd.close() + data = urlget(url) match = re.search(b"channelId=(\d+)", data) if match: @@ -34,9 +33,7 @@ class UStreamTV(Plugin): channelid = self._get_channel_id(self.url) if channelid: - fd = urllib.urlopen(self.AMFURL.format(channelid)) - data = fd.read() - fd.close() + data = urlget(self.AMFURL.format(channelid)) playpath = get_amf_value(data, "streamName") cdnurl = get_amf_value(data, "cdnUrl") diff --git a/src/livestreamer/utils.py b/src/livestreamer/utils.py index 60a4d6a..5ab8a63 100644 --- a/src/livestreamer/utils.py +++ b/src/livestreamer/utils.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from livestreamer.compat import urllib, bytes +from livestreamer.plugins import PluginError import hmac, hashlib, zlib, argparse SWF_KEY = b"Genuine Adobe Flash Player 001" @@ -36,10 +37,26 @@ class ArgumentParser(argparse.ArgumentParser): val = line[split+1:].strip() yield "--%s=%s" % (key, val) +def urlget(url, data=None, timeout=None, opener=None): + try: + if opener is not None: + fd = opener.open(url) + else: + fd = urllib.urlopen(url, data, timeout) + + data = fd.read() + fd.close() + + except IOError as err: + if type(err) is urllib.URLError: + raise PluginError(err.reason) + else: + raise PluginError(err) + + return data + def swfverify(url): - fd = urllib.urlopen(url) - swf = fd.read() - fd.close() + swf = urlget(url) if swf[:3] == b"CWS": swf = b"F" + swf[1:8] + zlib.decompress(swf[8:]) @@ -47,3 +64,4 @@ def swfverify(url): h = hmac.new(SWF_KEY, swf, hashlib.sha256) return h.hexdigest(), len(swf) + -- cgit v1.2.3