]> code.delx.au - webdl/blobdiff - sbs.py
Added license
[webdl] / sbs.py
diff --git a/sbs.py b/sbs.py
index b95a67b7555ca87db06d8863ccb0180102f9a1a5..bf599417e48bd565199fcd0586a9160c282ded72 100644 (file)
--- a/sbs.py
+++ b/sbs.py
 #!/usr/bin/env python
-# vim:ts=4:sts=4:sw=4:noet
 
-from common import grab_json, grab_xml, download_rtmp, Node
+from common import grab_text, grab_html, grab_json, grab_xml, download_hds, Node, append_to_qs
 
 import collections
+import urlparse
 
 BASE = "http://www.sbs.com.au"
-MENU_URL = "/api/video_feed/f/dYtmxB/%s?startIndex=%d"
-VIDEO_URL = BASE + "/api/video_feed/f/dYtmxB/CxeOeDELXKEv/%s?form=json"
-
-NS = {
-       "smil": "http://www.w3.org/2005/SMIL21/Language",
+VIDEO_MENU = BASE + "/ondemand/js/video-menu"
+VIDEO_URL = BASE + "/ondemand/video/single/%s"
+VIDEO_MAGIC = {
+    "v": "2.5.14",
+    "fp": "MAC 11,1,102,55",
+    "r": "FLQDD",
+    "g": "YNANAXRIYFYO",
 }
+SWF_URL = "http://resources.sbs.com.au/vod/theplatform/core/current/swf/flvPlayer.swf"
 
-SECTIONS = [
-       "section-sbstv",
-       "section-programs",
-]
-
-CATEGORY_MAP = {
-       "Factual": "Documentary",
+NS = {
+    "smil": "http://www.w3.org/2005/SMIL21/Language",
 }
 
 
 class SbsNode(Node):
-       def __init__(self, title, parent, video_id):
-               Node.__init__(self, title, parent)
-               self.title = title
-               self.video_id = video_id.split("/")[-1]
-               self.can_download = True
-
-       def download(self):
-               doc = grab_json(VIDEO_URL % self.video_id, 0)
-               best_url = None
-               best_bitrate = 0
-               for d in doc["media$content"]:
-                       bitrate = d["plfile$bitrate"]
-                       if bitrate > best_bitrate or best_url is None:
-                               best_bitrate = bitrate
-                               best_url = d["plfile$url"]
-
-               doc = grab_xml(best_url, 3600)
-               vbase = doc.xpath("//smil:meta/@base", namespaces=NS)[0]
-               vpath = doc.xpath("//smil:video/@src", namespaces=NS)[0]
-               ext = vpath.rsplit(".", 1)[1]
-               filename = self.title + "." + ext
-
-               return download_rtmp(filename, vbase, vpath)
-
-def fill_entry(get_catnode, entry):
-       title = entry["title"]
-       video_id = entry["id"]
-       info = collections.defaultdict(list)
-       for d in entry["media$categories"]:
-               if not d.has_key("media$scheme"):
-                       continue
-               info[d["media$scheme"]].append(d["media$name"])
-
-       if "Section/Promos" in info.get("Section", []):
-               # ignore promos
-               return
-
-       for category in info.get("Genre", ["$UnknownCategory$"]):
-               category = CATEGORY_MAP.get(category, category)
-               parent_node = get_catnode(category)
-               SbsNode(title, parent_node, video_id)
-
-
-def fill_section(get_catnode, section):
-       index = 1
-       while True:
-               doc = grab_json(BASE + MENU_URL % (section, index), 3600)
-               if len(doc.get("entries", [])) == 0:
-                       break
-               for entry in doc["entries"]:
-                       fill_entry(get_catnode, entry)
-               index += doc["itemsPerPage"]
+    def __init__(self, title, parent, video_id):
+        Node.__init__(self, title, parent)
+        self.title = title
+        self.video_id = video_id.split("/")[-1]
+        self.can_download = True
+
+    def download(self):
+        doc = grab_html(VIDEO_URL % self.video_id, 0)
+        meta_video = doc.xpath("//meta[@property='og:video']")[0]
+        swf_url = meta_video.attrib["content"]
+        swf_url_qs = urlparse.parse_qs(urlparse.urlparse(swf_url).query)
+        desc_url = swf_url_qs["v"][0]
+
+        doc = grab_text(desc_url, 0)
+        doc_qs = urlparse.parse_qs(doc)
+        desc_url = doc_qs["releaseUrl"][0]
+
+        doc = grab_xml(desc_url, 0)
+        error = doc.xpath("//smil:param[@name='exception']/@value", namespaces=NS)
+        if error:
+            raise Exception("Error downloading, SBS said: " + error[0])
+
+        video = doc.xpath("//smil:video", namespaces=NS)[0]
+        video_url = video.attrib["src"]
+        if not video_url:
+            raise Exception("Unsupported video '%s': %s" % (self.title, desc_url))
+        filename = self.title + ".flv"
+        video_url = append_to_qs(video_url, VIDEO_MAGIC)
+        return download_hds(filename, video_url, pvswf=SWF_URL)
+
+class SbsNavNode(Node):
+    def __init__(self, title, parent, url):
+        Node.__init__(self, title, parent)
+        self.url = url
+
+    def fill_children(self):
+        try:
+            doc = grab_json(BASE + self.url, 3600)
+        except ValueError:
+            # SBS sends XML as an error message :\
+            return
+        if len(doc.get("entries", [])) == 0:
+            return
+        for entry in doc["entries"]:
+            self.fill_entry(entry)
+
+    def fill_entry(self, entry):
+        title = entry["title"]
+        video_id = entry["id"]
+        SbsNode(title, self, video_id)
+
+class SbsRootNode(Node):
+    def __init__(self, parent):
+        Node.__init__(self, "SBS", parent)
+
+    def fill_children(self):
+        menu = grab_json(VIDEO_MENU, 3600, skip_assignment=True)
+        for name in menu.keys():
+            self.fill_category(self, menu[name])
+
+    def create_nav_node(self, name, parent, cat_data, url_key):
+        try:
+            url = cat_data[url_key]
+        except KeyError:
+            return
+        if url.strip():
+            SbsNavNode(name, parent, url)
+
+    def fill_category(self, parent, cat_data):
+        if not cat_data.has_key("children"):
+            name = cat_data["name"]
+            self.create_nav_node(name, parent, cat_data, "url")
+            return
+
+        node = Node(cat_data["name"], parent)
+        self.create_nav_node("-Featured", node, cat_data, "furl")
+        self.create_nav_node("-Latest", node, cat_data, "url")
+        self.create_nav_node("-Most Popular", node, cat_data, "purl")
+
+        children = cat_data.get("children", [])
+        if isinstance(children, dict):
+            children = [children[k] for k in sorted(children.keys())]
+        for child_cat in children:
+            self.fill_category(node, child_cat)
 
 def fill_nodes(root_node):
-       catnodes = {}
-       def get_catnode(name):
-               try:
-                       return catnodes[name]
-               except KeyError:
-                       n = Node(name, root_node)
-                       catnodes[name] = n
-                       return n
-
-       for section in SECTIONS:
-               fill_section(get_catnode, section)
-
+    SbsRootNode(root_node)