]> code.delx.au - webdl/blob - brightcove.py
Disable Channel 9 because it's been broken for a while
[webdl] / brightcove.py
1 import logging
2 import re
3 import sys
4
5 from common import grab_json, download_hls, download_hds, Node, append_to_qs
6
7 CH9_TOKEN = "ogxhPgSphIVa2hhxbi9oqtYwtg032io4B4-ImwddYliFWHqS0UfMEw.."
8 CH10_TOKEN = "90QPG7lQuLJAc4s82qA-T_UoDhz_VBFK6SGstWDB0jZH8eu1SZQDFA.."
9
10 BRIGHTCOVE_API = "http://api.brightcove.com/services/library?"
11
12
13 class BrightcoveVideoNode(Node):
14 def __init__(self, title, parent, token, video_id):
15 Node.__init__(self, title, parent)
16 self.can_download = True
17 self.token = token
18 self.video_id = video_id
19
20 def download(self):
21 f = None
22 try_streams = [self.try_hds, self.try_hls]
23
24 while f is None and try_streams:
25 f = try_streams.pop()()
26
27 if f is None:
28 logging.error("No HLS or HDS stream available for: %s", self.title)
29 return False
30
31 return f()
32
33 def try_hls(self):
34 desc_url = append_to_qs(BRIGHTCOVE_API, {
35 "token": self.token,
36 "command": "find_video_by_id",
37 "video_fields": "HLSURL",
38 "video_id": self.video_id,
39 })
40
41 doc = grab_json(desc_url)
42 video_url = doc and doc["HLSURL"]
43 if not video_url:
44 return
45
46 filename = self.title + ".ts"
47 return lambda: download_hls(filename, video_url)
48
49 def try_hds(self):
50 desc_url = append_to_qs(BRIGHTCOVE_API, {
51 "token": self.token,
52 "command": "find_video_by_id",
53 "video_fields": "hdsManifestUrl",
54 "video_id": self.video_id,
55 })
56
57 doc = grab_json(desc_url)
58 video_url = doc and doc["hdsManifestUrl"]
59 if not video_url:
60 return
61
62 filename = self.title + ".flv"
63 return lambda: download_hds(filename, video_url)
64
65
66 class BrightcoveRootNode(Node):
67 def __init__(self, title, parent, token):
68 Node.__init__(self, title, parent)
69 self.token = token
70 self.series_nodes = {}
71
72 def get_series_node(self, series_name):
73 series_name = series_name.split("-")[0].strip()
74 key = series_name.lower()
75 node = self.series_nodes.get(key, None)
76 if node is None:
77 node = Node(series_name, self)
78 self.series_nodes[key] = node
79 return node
80
81 def fill_children(self):
82 page_number = 0
83 while page_number < 100:
84 url = self.get_all_videos_url(page_number)
85 page_number += 1
86
87 page = grab_json(url)
88 items = page["items"]
89 if len(items) == 0:
90 break
91
92 for video_desc in items:
93 self.process_video(video_desc)
94
95 def process_video(self, video_desc):
96 if not video_desc["customFields"]:
97 return
98
99 video_id = video_desc["id"]
100 title = self.get_video_title(video_desc)
101 series_name = self.get_series_name(video_desc)
102
103 parent_node = self.get_series_node(series_name)
104 BrightcoveVideoNode(title, parent_node, self.token, video_id)
105
106 def get_video_title(self, video_desc):
107 raise NotImplementedError()
108
109 def get_series_name(self, video_desc):
110 raise NotImplementedError()
111
112 def get_all_videos_url(self, page_number):
113 raise NotImplementedError()
114
115
116 class Ch9RootNode(BrightcoveRootNode):
117 def __init__(self, root_node):
118 BrightcoveRootNode.__init__(self, "Nine", root_node, CH9_TOKEN)
119
120 def get_video_title(self, video_desc):
121 title = video_desc["name"]
122 season = video_desc["customFields"].get("season", "")
123 episode = video_desc["customFields"].get("episode", "1").rjust(2, "0")
124 series = self.get_series_name(video_desc)
125
126 if re.match(R"ep(isode)?\s*[0-9]+\s*:", title.lower()):
127 title = title.split(":", 1)[1].strip()
128
129 title = "%s - %sx%s - %s" % (
130 series,
131 season,
132 episode,
133 title
134 )
135 return title
136
137 def get_series_name(self, video_desc):
138 series = video_desc["customFields"].get("series", None)
139 if not series:
140 series = video_desc["name"]
141 return series
142
143 def get_all_videos_url(self, page_number):
144 return append_to_qs(BRIGHTCOVE_API, {
145 "token": self.token,
146 "command": "search_videos",
147 "video_fields": "id,name,customFields",
148 "custom_fields": "series,season,episode",
149 "sort_by": "PUBLISH_DATE",
150 "page_number": str(page_number),
151 })
152
153
154 class Ch10RootNode(BrightcoveRootNode):
155 def __init__(self, root_node):
156 BrightcoveRootNode.__init__(self, "Ten", root_node, CH10_TOKEN)
157
158 def get_video_title(self, video_desc):
159 return video_desc["name"]
160
161 def get_series_name(self, video_desc):
162 return video_desc["customFields"]["tv_show"]
163
164 def get_all_videos_url(self, page_number):
165 return append_to_qs(BRIGHTCOVE_API, {
166 "token": self.token,
167 "command": "search_videos",
168 "video_fields": "id,name,customFields",
169 "custom_fields": "tv_show",
170 "sort_by": "PUBLISH_DATE",
171 "any": "video_type_long_form:Full Episode",
172 "page_number": str(page_number),
173 })
174
175
176 def fill_nodes(root_node):
177 # Ch9RootNode(root_node) -- Need a new API token
178 Ch10RootNode(root_node)
179