From 79d4e6053995c15d79afbce24981eca430cf340d Mon Sep 17 00:00:00 2001 From: Christopher Rosell Date: Thu, 19 Jan 2012 22:52:08 +0100 Subject: livestreamer.plugins.justintv: Now supports subscription channels. Needs a cookie passed with --jtv-cookie to get a subscription key. --- src/livestreamer/plugins/justintv.py | 60 ++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/livestreamer/plugins/justintv.py b/src/livestreamer/plugins/justintv.py index dacfd05..9db0932 100644 --- a/src/livestreamer/plugins/justintv.py +++ b/src/livestreamer/plugins/justintv.py @@ -6,14 +6,19 @@ from livestreamer.compat import urllib, str import xml.dom.minidom, re, sys, random -class JustinTV(object): +class JustinTV(Plugin): StreamInfoURL = "http://usher.justin.tv/find/{0}.xml?type=any&p={1}" + StreamInfoURLSub = "http://usher.justin.tv/find/{0}.xml?type=any&p={1}&b_id=true&chansub_guid={2}&private_code=null&group=&channel_subscription={2}" + MetadataURL = "http://www.justin.tv/meta/{0}.xml?on_site=true" SWFURL = "http://www.justin.tv/widgets/live_embed_player.swf" def can_handle_url(self, url): return ("justin.tv" in url) or ("twitch.tv" in url) - def get_channel_name(self, url): + def handle_parser(self, parser): + parser.add_argument("--jtv-cookie", metavar="cookie", help="JustinTV cookie to allow access to subscription channels") + + def _get_channel_name(self, url): fd = urllib.urlopen(url) data = fd.read() fd.close() @@ -22,15 +27,40 @@ class JustinTV(object): if match: return str(match.group(1), "ascii") + def _get_metadata(self, channel, cookie=None): + if cookie: + headers = {"Cookie": cookie} + req = urllib.Request(self.MetadataURL.format(channel), headers=headers) + else: + req = urllib.Request(self.MetadataURL.format(channel)) - def get_streams(self, url): - def get_node_text(element): - res = [] - for node in element.childNodes: - if node.nodeType == node.TEXT_NODE: - res.append(node.data) - return "".join(res) + fd = urllib.urlopen(req) + data = fd.read() + fd.close() + + dom = xml.dom.minidom.parseString(data) + meta = dom.getElementsByTagName("meta")[0] + metadata = {} + + metadata["title"] = self._get_node_if_exists(dom, "title") + metadata["chansub_guid"] = self._get_node_if_exists(dom, "chansub_guid") + + return metadata + + def _get_node_if_exists(self, dom, name): + elements = dom.getElementsByTagName(name) + if elements and len(elements) > 0: + return self._get_node_text(elements[0]) + + def _get_node_text(self, element): + res = [] + for node in element.childNodes: + if node.nodeType == node.TEXT_NODE: + res.append(node.data) + return "".join(res) + + def get_streams(self, url): def clean_tag(tag): if tag[0] == "_": return tag[1:] @@ -38,12 +68,18 @@ class JustinTV(object): return tag randomp = int(random.random() * 999999) - channelname = self.get_channel_name(url) + channelname = self._get_channel_name(url) if not channelname: return False - fd = urllib.urlopen(self.StreamInfoURL.format(channelname, randomp)) + metadata = self._get_metadata(channelname, self.args.jtv_cookie) + + if "chansub_guid" in metadata: + fd = urllib.urlopen(self.StreamInfoURLSub.format(channelname, randomp, metadata["chansub_guid"])) + else: + fd = urllib.urlopen(self.StreamInfoURL.format(channelname, randomp)) + data = fd.read() fd.close() @@ -58,7 +94,7 @@ class JustinTV(object): for node in nodes.childNodes: stream = {} for child in node.childNodes: - stream[child.tagName] = get_node_text(child) + stream[child.tagName] = self._get_node_text(child) sname = clean_tag(node.tagName) streams[sname] = stream -- cgit v1.2.3