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