Integrating and testing of playback
[watersofshiloah] / src / index.py
1 import weakref
2 import logging
3
4 import util.misc as misc_utils
5 from util import go_utils
6 import backend
7
8
9 _moduleLogger = logging.getLogger(__name__)
10
11
12 class Connection(object):
13
14         def __init__(self):
15                 self._backend = backend.Backend()
16                 self._indexing = go_utils.AsyncPool()
17
18         def start(self):
19                 self._indexing.start()
20
21         def stop(self):
22                 self._indexing.stop()
23
24         def download(self, func, on_success, on_error, args = None, kwds = None):
25                 if args is None:
26                         args = ()
27                 if kwds is None:
28                         kwds = {}
29
30                 self._indexing.clear_tasks()
31                 self._indexing.add_task(
32                         getattr(self._backend, func),
33                         args,
34                         kwds,
35                         on_success,
36                         on_error,
37                 )
38
39
40 class AudioIndex(object):
41
42         def __init__(self):
43                 self._connection = Connection()
44                 self._languages = None
45                 self._languagesRequest = None
46                 self._sources = {}
47
48         def start(self):
49                 self._connection.start()
50
51         def stop(self):
52                 self._connection.stop()
53
54         def get_languages(self, on_success, on_error):
55                 if self._languages is None:
56                         assert self._languagesRequest is None
57                         self._languagesRequest = on_success, on_error
58                         self._connection.download(
59                                 "get_languages",
60                                 self._on_get_languages,
61                                 self._on_languages_error
62                         )
63                 else:
64                         on_success(self._languages)
65
66         def get_source(self, source, langId = None):
67                 key = (source, langId)
68                 if key in self._sources:
69                         node = self._sources[key]
70                 else:
71                         if source == "radio":
72                                 node = RadioNode(self._connection)
73                         elif source == "conferences":
74                                 assert langId is not None
75                                 node = ConferencesNode(self._connection, langId)
76                         else:
77                                 raise NotImplementedError(source)
78                         self._sources[key] = node
79
80                 return node
81
82         @misc_utils.log_exception(_moduleLogger)
83         def _on_get_languages(self, languages):
84                 assert self._languages is None
85                 assert self._languagesRequest is not None
86                 r = self._languagesRequest
87                 self._languagesRequest = None
88                 self._languages = languages
89                 r[0](self._languages)
90
91         @misc_utils.log_exception(_moduleLogger)
92         def _on_languages_error(self, e):
93                 assert self._languages is None
94                 assert self._languagesRequest is not None
95                 r = self._languagesRequest
96                 self._languagesRequest = None
97                 r[1](self._languages)
98
99
100 class Node(object):
101
102         def __init__(self, connection, parent, data):
103                 self._connection = connection
104                 self._parent = weakref.ref(parent) if parent is not None else None
105                 self._data = data
106                 self._children = None
107
108         def get_children(self, on_success, on_error):
109                 if self._children is None:
110                         self._get_children(on_success, on_error)
111                 else:
112                         on_success(self._children)
113
114         def get_parent(self):
115                 if self._parent is None:
116                         raise RuntimeError("")
117                 parent = self._parent()
118                 return parent
119
120         def get_properties(self):
121                 return self._data
122
123         @property
124         def title(self):
125                 raise NotImplementedError("On %s" % type(self))
126
127         def is_leaf(self):
128                 raise NotImplementedError("")
129
130         def _get_children(self, on_success, on_error):
131                 raise NotImplementedError("")
132
133
134 class ParentNode(Node):
135
136         def __init__(self, connection, parent, data):
137                 Node.__init__(self, connection, parent, data)
138                 self._request = None
139
140         def is_leaf(self):
141                 return False
142
143         def _get_children(self, on_success, on_error):
144                 assert self._request is None
145                 assert self._children is None
146                 self._request = on_success, on_error
147
148                 func, args, kwds = self._get_func()
149
150                 self._connection.download(
151                         func,
152                         self._on_success,
153                         self._on_error,
154                         args,
155                         kwds,
156                 )
157
158         def _get_func(self):
159                 raise NotImplementedError()
160
161         def _create_child(self, data):
162                 raise NotImplementedError()
163
164         @misc_utils.log_exception(_moduleLogger)
165         def _on_success(self, data):
166                 r = self._request
167                 self._request = None
168                 try:
169                         self._children = [
170                                 self._create_child(child)
171                                 for child in data
172                         ]
173                 except Exception, e:
174                         _moduleLogger.exception("Translating error")
175                         self._children = None
176                         r[1](e)
177                 else:
178                         r[0](self._children)
179
180         @misc_utils.log_exception(_moduleLogger)
181         def _on_error(self, error):
182                 r = self._request
183                 self._request = None
184                 r[1](error)
185
186
187 class LeafNode(Node):
188
189         def __init__(self, connection, parent, data):
190                 Node.__init__(self, connection, parent, data)
191
192         def is_leaf(self):
193                 return True
194
195         @property
196         def can_navigate(self):
197                 raise NotImplementedError("On %s" % type(self))
198
199         @property
200         def subtitle(self):
201                 raise NotImplementedError("On %s" % type(self))
202
203         @property
204         def uri(self):
205                 raise NotImplementedError("On %s" % type(self))
206
207         def _get_children(self, on_success, on_error):
208                 raise RuntimeError("Not is a leaf")
209
210
211 class RadioNode(ParentNode):
212
213         def __init__(self, connection):
214                 ParentNode.__init__(self, connection, None, {})
215
216         @property
217         def title(self):
218                 return "Radio"
219
220         def _get_func(self):
221                 return "get_radio_channels", (), {}
222
223         def _create_child(self, data):
224                 return RadioChannelNode(self._connection, self, data)
225
226
227 class RadioChannelNode(LeafNode):
228
229         def __init__(self, connection, parent, data):
230                 LeafNode.__init__(self, connection, parent, data)
231                 self._extendedData = {}
232                 self._request = None
233
234         @property
235         def can_navigate(self):
236                 return False
237
238         @property
239         def title(self):
240                 return "Radio"
241
242         @property
243         def subtitle(self):
244                 return ""
245
246         @property
247         def uri(self):
248                 return self._data["url"]
249
250         def get_programming(self, date, on_success, on_error):
251                 date = date.strftime("%Y-%m-%d")
252                 try:
253                         programming = self._extendedData[date]
254                 except KeyError:
255                         self._get_programming(date, on_success, on_error)
256                 else:
257                         on_success(programming)
258
259         def _get_programming(self, date, on_success, on_error):
260                 assert self._request is None
261                 assert date not in self._extendedData
262                 self._request = on_success, on_error, date
263
264                 self._connection.download(
265                         "get_radio_channel_programming",
266                         self._on_success,
267                         self._on_error,
268                         (self._data["id"], date),
269                         {},
270                 )
271
272         @misc_utils.log_exception(_moduleLogger)
273         def _on_success(self, data):
274                 r = self._request
275                 date = r[2]
276                 self._request = None
277                 try:
278                         self._extendedData[date] = [
279                                 child
280                                 for child in data
281                         ]
282                 except Exception, e:
283                         _moduleLogger.exception("Translating error")
284                         del self._extendedData[date]
285                         r[1](e)
286                 else:
287                         r[0](self._extendedData[date])
288
289         @misc_utils.log_exception(_moduleLogger)
290         def _on_error(self, error):
291                 r = self._request
292                 self._request = None
293                 r[1](error)
294
295
296 class ConferencesNode(ParentNode):
297
298         def __init__(self, connection, langId):
299                 ParentNode.__init__(self, connection, None, {})
300                 self._langId = langId
301
302         @property
303         def title(self):
304                 return "Conferences"
305
306         def _get_func(self):
307                 return "get_conferences", (self._langId, ), {}
308
309         def _create_child(self, data):
310                 return ConferenceNode(self._connection, self, data)
311
312
313 class ConferenceNode(ParentNode):
314
315         def __init__(self, connection, parent, data):
316                 ParentNode.__init__(self, connection, parent, data)
317
318         @property
319         def title(self):
320                 return self._data["title"]
321
322         def _get_func(self):
323                 return "get_conference_sessions", (self._data["id"], ), {}
324
325         def _create_child(self, data):
326                 return SessionNode(self._connection, self, data)
327
328
329 class SessionNode(ParentNode):
330
331         def __init__(self, connection, parent, data):
332                 ParentNode.__init__(self, connection, parent, data)
333
334         @property
335         def title(self):
336                 return self._data["title"]
337
338         def _get_func(self):
339                 return "get_conference_talks", (self._data["id"], ), {}
340
341         def _create_child(self, data):
342                 return TalkNode(self._connection, self, data)
343
344
345 class TalkNode(LeafNode):
346
347         def __init__(self, connection, parent, data):
348                 LeafNode.__init__(self, connection, parent, data)
349
350         @property
351         def can_navigate(self):
352                 return True
353
354         @property
355         def title(self):
356                 return self._data["title"]
357
358         @property
359         def subtitle(self):
360                 return self._data["speaker"]
361
362         @property
363         def uri(self):
364                 return self._data["url"]