]> code.delx.au - webdl/blob - iview.py
iView follows series links to find all the episodes
[webdl] / iview.py
1 from common import grab_json, grab_xml, Node, download_hls
2 import requests_cache
3 import urllib.parse
4
5 API_URL = "http://iview.abc.net.au/api"
6 AUTH_URL = "http://iview.abc.net.au/auth"
7
8 def format_episode_title(series, ep):
9 if ep:
10 return series + " " + ep
11 else:
12 return series
13
14 def add_episode(parent, ep_info):
15 video_key = ep_info["episodeHouseNumber"]
16 series_title = ep_info["seriesTitle"]
17 title = ep_info.get("title", None)
18 episode_title = format_episode_title(series_title, title)
19
20 IviewEpisodeNode(episode_title, parent, video_key)
21
22 class IviewEpisodeNode(Node):
23 def __init__(self, title, parent, video_key):
24 Node.__init__(self, title, parent)
25 self.video_key = video_key
26 self.filename = title + ".ts"
27 self.can_download = True
28
29 def find_hls_url(self, playlist):
30 for video in playlist:
31 if video["type"] == "program":
32 return video["hls-high"].replace("http:", "https:")
33 raise Exception("Missing hls-high program stream for " + self.video_key)
34
35 def get_auth_details(self):
36 with requests_cache.disabled():
37 auth_doc = grab_xml(AUTH_URL)
38 NS = {
39 "auth": "http://www.abc.net.au/iView/Services/iViewHandshaker",
40 }
41 token = auth_doc.xpath("//auth:tokenhd/text()", namespaces=NS)[0]
42 token_url = auth_doc.xpath("//auth:server/text()", namespaces=NS)[0]
43 token_hostname = urllib.parse.urlparse(token_url).netloc
44 return token, token_hostname
45
46 def add_auth_token_to_url(self, video_url, token, token_hostname):
47 parsed_url = urllib.parse.urlparse(video_url)
48 hacked_url = parsed_url._replace(netloc=token_hostname, query="hdnea=" + token)
49 video_url = urllib.parse.urlunparse(hacked_url)
50 return video_url
51
52 def download(self):
53 info = grab_json(API_URL + "/programs/" + self.video_key)
54 video_url = self.find_hls_url(info["playlist"])
55 token, token_hostname= self.get_auth_details()
56 video_url = self.add_auth_token_to_url(video_url, token, token_hostname)
57 return download_hls(self.filename, video_url)
58
59 class IviewIndexNode(Node):
60 def __init__(self, title, parent, url):
61 Node.__init__(self, title, parent)
62 self.url = url
63 self.unique_series = set()
64
65 def fill_children(self):
66 info = grab_json(self.url)
67 for index_list in info["index"]:
68 for ep_info in index_list["episodes"]:
69 self.add_series(ep_info)
70
71 def add_series(self, ep_info):
72 title = ep_info["seriesTitle"]
73 if title in self.unique_series:
74 return
75 self.unique_series.add(title)
76 url = API_URL + "/" + ep_info["href"]
77 IviewSeriesNode(title, self, url)
78
79 class IviewSeriesNode(Node):
80 def __init__(self, title, parent, url):
81 Node.__init__(self, title, parent)
82 self.url = url
83
84 def fill_children(self):
85 ep_info = grab_json(self.url)
86 series_slug = ep_info["href"].split("/")[1]
87 series_url = API_URL + "/series/" + series_slug + "/" + ep_info["seriesHouseNumber"]
88 info = grab_json(series_url)
89 for ep_info in info["episodes"]:
90 add_episode(self, ep_info)
91
92 class IviewFlatNode(Node):
93 def __init__(self, title, parent, url):
94 Node.__init__(self, title, parent)
95 self.url = url
96
97 def fill_children(self):
98 info = grab_json(self.url)
99 for ep_info in info:
100 add_episode(self, ep_info)
101
102
103 class IviewRootNode(Node):
104 def load_categories(self):
105 by_category_node = Node("By Category", self)
106 def category(name, slug):
107 IviewIndexNode(name, by_category_node, API_URL + "/category/" + slug)
108
109 category("Arts & Culture", "arts")
110 category("Comedy", "comedy")
111 category("Documentary", "docs")
112 category("Drama", "drama")
113 category("Education", "education")
114 category("Lifestyle", "lifestyle")
115 category("News & Current Affairs", "news")
116 category("Panel & Discussion", "panel")
117 category("Sport", "sport")
118
119 def load_channels(self):
120 by_channel_node = Node("By Channel", self)
121 def channel(name, slug):
122 IviewIndexNode(name, by_channel_node, API_URL + "/channel/" + slug)
123
124 channel("ABC1", "abc1")
125 channel("ABC2", "abc2")
126 channel("ABC3", "abc3")
127 channel("ABC4Kids", "abc4kids")
128 channel("News", "news")
129 channel("ABC Arts", "abcarts")
130 channel("iView Exclusives", "iview")
131
132 def load_featured(self):
133 IviewFlatNode("Featured", self, API_URL + "/featured")
134
135 def fill_children(self):
136 self.load_categories()
137 self.load_channels()
138 self.load_featured()
139
140
141 def fill_nodes(root_node):
142 IviewRootNode("ABC iView", root_node)
143