zsh: Whitespace editing
[shutils.git] / oe1archive.py
1 #!/usr/bin/env python3
2
3 """A simple tool to query the Oe1 7 Tage archive."""
4
5 __version__ = "2.0"
6 __author__ = "Stefan Huber"
7
8
9 import urllib.request
10 import simplejson
11 import dateutil.parser
12 import sys
13 import getopt
14 import re
15 import os
16
17
18 class Archive:
19
20 def __init__(self):
21 self.json = read_json("http://audioapi.orf.at/oe1/json/2.0/broadcasts/")
22
23 def get_days(self):
24 return map(_json_to_day, self.json)
25
26 def get_broadcasts(self, day):
27 bjson = self.json[day]['broadcasts']
28 return map(_json_to_broadcast, bjson)
29
30 def get_broadcast(self, day, broadcast):
31 return _json_to_broadcast(self.json[day]['broadcasts'][broadcast])
32
33 def get_player_url(self, day, broadcast):
34 date = self.json[day]['day']
35 pk = self.json[day]['broadcasts'][broadcast]['programKey']
36 url = "http://oe1.orf.at/player/%d/%s"
37 return url % (date, pk)
38
39 def get_broadcast_subtitle(self, day, broadcast):
40 return self.json[day]['broadcasts'][broadcast]['subtitle']
41
42 def get_broadcast_pk(self, day, broadcast):
43 return self.json[day]['broadcasts'][broadcast]['programKey']
44
45 def get_broadcast_url(self, day, broadcast):
46 date = self.json[day]['day']
47 pk = self.json[day]['broadcasts'][broadcast]['programKey']
48
49 burl = 'https://audioapi.orf.at/oe1/api/json/current/broadcast/%s/%d'
50 bjson = read_json(burl % (pk, date))
51
52 sjson = bjson['streams']
53 if len(sjson) == 0:
54 return None
55
56 sid = sjson[0]['loopStreamId']
57 surl = 'http://loopstream01.apa.at/?channel=oe1&shoutcast=0&id=%s'
58 return surl % sid
59
60 def get_broadcast_description(self, day, broadcast):
61 date = self.json[day]['day']
62 pk = self.json[day]['broadcasts'][broadcast]['programKey']
63
64 burl = 'https://audioapi.orf.at/oe1/api/json/current/broadcast/%s/%d'
65 bjson = read_json(burl % (pk, date))
66
67 description = bjson['description']
68 akm = bjson['akm']
69 if description is None:
70 description = ""
71 if akm is None:
72 akm = ""
73 return description + "<br>" + akm;
74
75 def get_broadcasts_by_regex(self, key):
76 rex = re.compile(key, re.IGNORECASE)
77
78 res = []
79 for d, djson in enumerate(self.json):
80 for b, bjson in enumerate(djson['broadcasts']):
81 if rex.search(bjson['title']) is not None:
82 res.append((d, b))
83 elif bjson['subtitle'] is not None and rex.search(bjson['subtitle']) is not None:
84 res.append((d, b))
85 return res
86
87 def _json_to_day(djson):
88 return dateutil.parser.parse(djson['dateISO'])
89
90 def _json_to_broadcast(bjson):
91 dt = dateutil.parser.parse(bjson['startISO'])
92 return (dt, bjson['title'])
93
94
95 def read_json(url):
96 with urllib.request.urlopen(url) as f:
97 dec = simplejson.JSONDecoder()
98 return dec.decode(f.read())
99
100 def input_index(prompt, li):
101 while True:
102 try:
103 idx = int(input(prompt))
104 if idx < 0 or idx >= len(li):
105 print("Out out range!")
106 else:
107 return idx
108
109 except ValueError:
110 print("Unknown input.")
111 except EOFError:
112 sys.exit(1)
113
114 def screen_help():
115 print("""Usage:
116 {0} -h, --help
117 {0} -c, --choose
118 {0} -s, --search TITLE""".format(sys.argv[0]))
119
120 def screen_choose():
121 a = Archive()
122
123 print("Choose a date:")
124 days = list(a.get_days())
125 for i, date in enumerate(days):
126 print(" [%d] %s" % (i, date.strftime("%a %d. %b %Y")))
127 day = input_index("Date: ", days)
128 chosen_datetime = days[day]
129 print()
130
131 print("Choose a broadcast:")
132 broadcasts = list(a.get_broadcasts(day))
133 for i, b in enumerate(broadcasts):
134 date, title = b
135 print(" [%2d] %s %s" % (i, date.strftime("%H:%M:%S"), title))
136 broadcast = input_index("Broadcast: ", broadcasts)
137 print()
138
139 print_broadcast_info(a, day, broadcast)
140 print()
141
142 url = a.get_broadcast_url(day, broadcast)
143 if url is not None:
144 answer = input("Do you want to download the chosen broadcast? (y/N) ")
145 if answer in ["y", "Y", "j", "J"]:
146 name = input("Download directory (prefix): ")
147
148 try:
149 dirname = get_directory_name(name, chosen_datetime)
150 print("Downloading to %s..." % dirname)
151
152 make_directory(name, chosen_datetime)
153
154 description = a.get_broadcast_description(day, broadcast)
155 write_html_file(name, chosen_datetime, description)
156
157 write_mp3_file(name, chosen_datetime, url)
158
159 except OSError as e:
160 print("Error creating directory.")
161 print(e)
162
163 except requests.exceptions.RequestException as e:
164 print("Request getting mp3 failed.")
165
166 except Exception as e:
167 print("Error downloading mp3.")
168 print(e)
169
170 def get_directory_name(name, datetime):
171 prefix = ""
172 if len(name) > 0:
173 prefix = name + "_"
174
175 return prefix + datetime.strftime("%Y-%m-%d")
176
177 def make_directory(name, datetime):
178 """Creates the download subdirectory for the given name and datetime."""
179 dirname = get_directory_name(name, datetime)
180 if not os.path.exists(dirname):
181 os.makedirs(dirname)
182
183 def write_html_file(name, datetime, description):
184 """Stores broadcast description into a html file."""
185
186 longname = get_directory_name(name, datetime)
187 filepath = os.path.join(longname, "description.html")
188 file = open(filepath, 'w+')
189 file.write("<!DOCTYPE html>\n")
190 file.write("<html>\n")
191 file.write("<head>\n")
192 file.write("<title>\n")
193 file.write("%s %s\n" % (name, datetime.strftime("%d.%m.%Y")))
194 file.write("</title>\n")
195 file.write("<meta charset = \"utf-8\">\n")
196 file.write("</head>\n")
197 file.write("<body>\n")
198 file.write("%s %s" % (name, datetime.strftime("%d.%m.%Y")))
199 file.write(description)
200 file.write("</body>\n")
201 file.write("</html>")
202 file.close()
203
204 def write_mp3_file(name, datetime, url):
205 import requests
206
207 longname = get_directory_name(name, datetime)
208 filepath = os.path.join(longname, "stream.mp3")
209
210 print("Fetching mp3...")
211 r = requests.get(url, stream=True)
212 if r.status_code == 200:
213 with open(filepath, 'wb') as f:
214 f.write(r.content)
215 else:
216 print("Error downloading mp3. Status code: %d" % r.status_code)
217
218 def screen_search(key):
219 a = Archive()
220 for d, b in a.get_broadcasts_by_regex(key):
221 print_broadcast_info(a, d, b)
222 print()
223
224 def print_broadcast_info(archive, day, broadcast):
225 a, d, b = archive, day, broadcast
226 date, title = a.get_broadcast(d, b)
227
228 print("%s %s" % (date.strftime("%a %d.%m.%Y %H:%M:%S"), title))
229 print(" %s" % a.get_broadcast_subtitle(d, b))
230 print(" Broadcast: %s" % a.get_broadcast_url(d, b))
231 print(" Player: %s" % a.get_player_url(d, b))
232 print(" Program key: %s" % a.get_broadcast_pk(d, b))
233
234 if __name__ == "__main__":
235
236 try:
237 opts, args = getopt.getopt(sys.argv[1:], "hcs:",
238 ["help", "choose", "search="])
239 except getopt.GetoptError as err:
240 print(err)
241 screen_help()
242 sys.exit(2)
243
244 for o, a in opts:
245 if o in ["-h", "--help"]:
246 screen_help()
247 if o in ["-c", "--choose"]:
248 screen_choose()
249 if o in ["-s", "--search"]:
250 screen_search(a)