]> code.delx.au - webdl/blob - sbs.py
hls: allow last segment to be missing
[webdl] / sbs.py
1 #!/usr/bin/env python
2
3 from common import grab_text, grab_html, grab_json, grab_xml, download_rtmp, download_urllib, Node, append_to_qs
4
5 import collections
6 import urlparse
7
8 BASE = "http://www.sbs.com.au"
9 VIDEO_MENU = BASE + "/ondemand/js/video-menu"
10 VIDEO_URL = BASE + "/ondemand/video/single/%s"
11 VIDEO_MAGIC = {
12 "v": "2.5.14",
13 "fp": "MAC 11,1,102,55",
14 "r": "FLQDD",
15 "g": "YNANAXRIYFYO",
16 }
17 SWF_URL = "http://resources.sbs.com.au/vod/theplatform/core/current/swf/flvPlayer.swf"
18
19 NS = {
20 "smil": "http://www.w3.org/2005/SMIL21/Language",
21 }
22
23
24 class SbsNode(Node):
25 def __init__(self, title, parent, video_id):
26 Node.__init__(self, title, parent)
27 self.title = title
28 self.video_id = video_id.split("/")[-1]
29 self.can_download = True
30
31 def download(self):
32 doc = grab_html(VIDEO_URL % self.video_id, 0)
33 meta_video = doc.xpath("//meta[@property='og:video']")[0]
34 swf_url = meta_video.attrib["content"]
35 swf_url_qs = urlparse.parse_qs(urlparse.urlparse(swf_url).query)
36 desc_url = swf_url_qs["v"][0]
37
38 doc = grab_text(desc_url, 0)
39 doc_qs = urlparse.parse_qs(doc)
40 desc_url = doc_qs["releaseUrl"][0]
41
42 doc = grab_xml(desc_url, 0)
43 error = doc.xpath("//smil:param[@name='exception']/@value", namespaces=NS)
44 if error:
45 raise Exception("Error downloading, SBS said: " + error[0])
46
47 video = doc.xpath("//smil:video", namespaces=NS)[0]
48 video_url = video.attrib["src"]
49 if not video_url:
50 raise Exception("Unsupported video '%s': %s" % (self.title, desc_url))
51 ext = urlparse.urlsplit(video_url).path.rsplit(".", 1)[1]
52 filename = self.title + "." + ext
53 video_url = append_to_qs(video_url, VIDEO_MAGIC)
54 return download_urllib(filename, video_url, referrer=SWF_URL)
55
56 class SbsNavNode(Node):
57 def __init__(self, title, parent, url):
58 Node.__init__(self, title, parent)
59 self.url = url
60
61 def fill_children(self):
62 try:
63 doc = grab_json(BASE + self.url, 3600)
64 except ValueError:
65 # SBS sends XML as an error message :\
66 return
67 if len(doc.get("entries", [])) == 0:
68 return
69 for entry in doc["entries"]:
70 self.fill_entry(entry)
71
72 def fill_entry(self, entry):
73 title = entry["title"]
74 video_id = entry["id"]
75 SbsNode(title, self, video_id)
76
77 class SbsRootNode(Node):
78 def __init__(self, parent):
79 Node.__init__(self, "SBS", parent)
80
81 def fill_children(self):
82 menu = grab_json(VIDEO_MENU, 3600, skip_assignment=True)
83 for name in menu.keys():
84 self.fill_category(self, menu[name])
85
86 def create_nav_node(self, name, parent, cat_data, url_key):
87 try:
88 url = cat_data[url_key]
89 except KeyError:
90 return
91 if url.strip():
92 SbsNavNode(name, parent, url)
93
94 def fill_category(self, parent, cat_data):
95 if not cat_data.has_key("children"):
96 name = cat_data["name"]
97 self.create_nav_node(name, parent, cat_data, "url")
98 return
99
100 node = Node(cat_data["name"], parent)
101 self.create_nav_node("-Featured", node, cat_data, "furl")
102 self.create_nav_node("-Latest", node, cat_data, "url")
103 self.create_nav_node("-Most Popular", node, cat_data, "purl")
104
105 children = cat_data.get("children", [])
106 if isinstance(children, dict):
107 children = [children[k] for k in sorted(children.keys())]
108 for child_cat in children:
109 self.fill_category(node, child_cat)
110
111 def fill_nodes(root_node):
112 SbsRootNode(root_node)
113