-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinfoq.py
466 lines (419 loc) · 47.8 KB
/
infoq.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import codecs
import os
import re
import socket
import sys
from string import Template
import urllib2
import netlib
INFOQ_PNG_BASE64 = 'data:image/png;base64,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o0mx5SzsmcI/s25VNwxVJwZrk0PV8YHa6MCRO/cWlxEpDZNK5MhHhRRsv/rVr/7qV7/6+c9/fnBwcHp6empqqneKbNOmTT/xEz/xf//3f7fffntxrjp27Ng999wTyHJJqfQ7v/M7l1566V/8xV/MzMycf/75SQ0YGxsDyL/7u7/7/vvvf+UrX/mmN73pbW97m649TCJNgk30qle9CmC7+eabwdwXX3zxm9/8ZnA8PvOpmoGBgdHRUZb3lEmlWfTInde+9rVQs3//938fvnnKKae8733vw0jhE6O2efPml7zkJZIbRxVpTHqZBw8exOACvWgMRN5f/uVf4ntvIU7DXt4/i399sAVS5oGtTKIUXGbYarX+93//l0YORUHRfd/3fd9tt912+eWXgyP/53/+53Of+xzu/PRP//Qll1wCSfzpT38ar/zMz/wMbKr/+q//wpNQRFddddWBAweAKygi0BfOAGgKMXno0KF/+Zd/OXr06Mte9rJKpQIv/9prr0UJjzzyCNgCrYLFeOONN+LLv//7v3/ta19DG17xilcAFc8999zf/u3fopyg5eeeey409oc//GF8/+xnP0tsBJ770R/90ZNPPvnOO+/ET2j/T/7kT+Lzda973RVXXPHyl7/89NNPh2CCLX3HHXdATtm2jZbACP/P//xPlIPaoashEa6//npw59133/3BD34Qd6688kq0f3h4GNoVzYBQQC0wUv71X/+V9v7uhE/8Afvyl7+MZ0gh//qv//oP/uAP3uZfZ5111utf/3o8A+5HvagCfSQfCcXCWgb1XvziF3/7t387GoAS4Bd4kyl2J0CKln/P93wPyrz11lv/+7//27Is8CsQDiH7vd/7vSDyP/7jP+JFdBOYB2V+/Md/fM+ePagLowA1vnfv3n/+538GAUENDAR69I1vfAO9Q/mgMIiPlnzXd30Xyvm7v/s7APUXf/EXIYNAmU996lMYHc95M81f+IVfgJBCLwJP5BOf+AR1H+wBTYjSbrrpJgxcrVZ7zWteA9tt69at11xzDUiNNkMooN4vfvGLMExQMuQpRgENRtdgpzz44INo80knnfTxj38cAp1oAmZDw0488UQgE+TCyOJhmCSgEsb6n/7pn9BH9BrmFSqCo4TOYnwhXJ566ikoIYwamg1GxcNgXXRfI1KYllTKHKIus1ZwKgYA4wfagS4Ytl/6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'
class Interview(object):
'InfoQ Interview Processing'
TITLE_RE = re.compile(r'<title>(.*)</title>')
TIMES_RE = re.compile(r'\s+TIMES\s*=\s*new\s+Array\s*\(.*\)')
MP4_RE = re.compile(r'<\s*source\s+src\s*=\s*"(http://.*/.+\.mp4)"\s*/>')
QUESTION_RE = re.compile(r'<\s*div\s+id\s*=\s*"question\d+"\s+class\s*=\s*"question"\s*>')
SPAN_RE = re.compile(r'<\s*span\s+onclick\s*=\s*"goToTime\((\d+)\);"\s*>(.*)</span>')
ANSWER_RE = re.compile(r'<\s*div\s+id\s*=\s*"answer\d+"\s+class\s*=\s*"answer"\s*>')
TAG_RE = re.compile(r'</?.+')
START_DIV_RE = re.compile(r'<\s*div\s+')
def reset(self):
self.checked = {'title': False,'mp4': False, 'TIMES': False, 'QA': False}
self.url = None
self.TIMES_JS = None
self.title = None
self.mp4_url = None
self.questions = []
self.answers = []
self.qa_times = []
def __init__(self, server=None, port='80'):
self.reset()
self.server = server
self.port = port
def all_checked(self):
for flag in self.checked:
if not self.checked[flag]:
return False
return True
def make_player(self, name='player.html'):
player_str = '''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
<style>
body{color:#000;font:1em Lucida, Lucida Grande, Tahoma, sans-serif;width:930px;margin-left:auto;margin-right:auto;}
#steps{width:100%;padding-top:5px}
#interviewPlayer{width:320px; height: 265px;float: left; margin-right: 10px;}
#intTranscript{padding: 5px; max-height: 250px; height: 250px; max-width:600px; overflow: auto;}
div.question{padding:5px 5px;font-weight:bold;border-bottom:1px dashed #eeeeee;background-color:rgb(255, 255, 255);}
span.question{cursor:pointer;}
span.question:hover{text-decoration:none;color:#318528;}
div.answer{color: rgb(60, 60, 60);padding: 5px 0px;font-weight: normal}
</style>
<script type="text/javascript">
var flag = false;
${TIMES}
function goToTime(idx){
if (flag) {
var player=document.getElementById('player');
if (TIMES[idx]<0 || TIMES[idx]>player.duration) return;
player.currentTime=TIMES[idx];
player.play();
}
};
function init(){
var player=document.getElementById('player');
if (player && player.canPlayType && player.canPlayType('video/mp4').replace(/no/, '')) {
player.load();
player.addEventListener('canplaythrough', function(){
flag = true;
});
player.play();
};
};
</script>
</head>
<body onload="init();">
<div id="steps">
<a href="${url}" target="_blank">${title}</a>
<p>
<b>${HOWTO}</b>
<div>${download}<a href="${mp4_url}" target="_blank">${video}</a>${step1}</div>
<div>${step2}</div>
<div style="margin-bottom:15px;">${step3}</div>
</div>
<div id="interviewPlayer">
<video id="player" width="320" height="265" controls poster=${infoq_logo}>
<source id="source" src="${mp4}" type="video/mp4" />
${hints}
</video>
</div>
<div id="intTranscript">
${qa}
</div>
</body>
</html>'''
qa_str = '''
<div id="question${qid}" class="question">
<div width="100%"><span class="question" onclick="goToTime(${qid});">${question}</span></div>
<div id="answer${qid}" class="answer">${answer}</div>
</div>
'''
if self.mp4_url is not None and len(self.questions)>0:
VIDEO_RE = re.compile(r'/.*/([a-zA-z0-9].*\.mp4)$')
result = VIDEO_RE.search(self.mp4_url)
if result:
try:
file = codecs.open(os.curdir + os.sep + name, 'w', encoding='utf-8')
qa = ''
for idx in range(len(self.questions)):
if idx < len(self.answers):
answer = self.answers[idx]
else:
answer =''
qa = qa + Template(qa_str).substitute(dict(qid=idx, \
question=unicode(self.questions[idx],'utf-8'), \
answer=unicode(answer,'utf-8')))
player_dict = dict(url=unicode(self.url,'utf-8'), \
title=unicode(self.title,'utf-8'), \
HOWTO=_('HOWTO'), \
TIMES=unicode(self.TIMES_JS,'utf-8'), \
download = _('1. download '), \
video = _('video'), \
step1 = _(' to a directory.'), \
step2 = _('2. copy player.html to the same directory.'), \
step3 = _('3. browse player.html with Chrome, Safari, or IE 9+.'), \
hints = _('Your browser does not support HTML5 video. <br>Please use Chrome, Safari, or IE 9+.'),\
mp4=unicode(result.group(1),'utf-8'), \
mp4_url=unicode(self.mp4_url,'utf-8'), \
qa=qa, \
infoq_logo=INFOQ_PNG_BASE64)
file.write(Template(player_str).substitute(player_dict))
file.close()
except (ValueError,IOError), (err):
print >> sys.stderr, err
def parse(self, url):
file = netlib.urlfetch(url, ua=netlib.IPAD_UA, server=self.server, port=self.port)
if file is not None:
self.reset()
qa_status = 0
has_qa = False
self.url = url
try:
for line in file:
if not self.checked['title']:
result = Interview.TITLE_RE.search(line)
if result:
self.title = result.group(1)
self.checked['title'] = True
if self.all_checked():
break
else:
continue
if not self.checked['TIMES']:
result = Interview.TIMES_RE.search(line)
if result:
self.TIMES_JS = result.group(0)
self.TIMES_JS = 'var '+self.TIMES_JS+';'
self.checked['TIMES'] = True
if self.all_checked():
break
else:
continue
if not self.checked['mp4']:
result = Interview.MP4_RE.search(line)
if result:
self.mp4_url = result.group(1)
self.checked['mp4'] = True
if self.all_checked():
break
else:
continue
if not self.checked['QA']:
result = Interview.QUESTION_RE.search(line)
if result:
qa_status = 1
elif qa_status == 1:
result = Interview.SPAN_RE.search(line)
if result:
self.qa_times.append(result.group(1))
self.questions.append(result.group(2))
qa_status = 2
elif qa_status == 2:
result = Interview.ANSWER_RE.search(line)
if result:
qa_status = 3
elif qa_status == 3:
if not Interview.TAG_RE.match(line):
self.answers.append(line)
qa_status = 0
has_qa = True
elif has_qa:
result = Interview.START_DIV_RE.search(line)
if result:
self.checked['QA'] = True
if self.all_checked():
break
else:
continue
except (urllib2.URLError, socket.error, socket.herror, socket.gaierror, socket.timeout), (err):
print >> sys.stderr, err
#output
if self.mp4_url is not None:
print >> sys.stdout, self.mp4_url
else:
print >> sys.stderr, _('NO mp4 found.')
self.make_player()
### END class Interview ###
#
#
#
class Presentation(object):
'InfoQ Presentation Processing'
TITLE_RE = re.compile(r'<title>(.*)</title>')
SLIDES_RE = re.compile(r'\s+slides\s*=\s*new\s+Array\s*\((.*)\)')
TIMES_RE = re.compile(r'\s+TIMES\s*=\s*new\s+Array\s*\(.*\)')
MP4_RE = re.compile(r'<\s*source\s+src\s*=\s*"(http://.*/.+\.mp4)"\s*/>')
def reset(self):
self.checked = {'title': False,'mp4': False, 'TIMES': False, 'slides': False}
self.url = None
self.TIMES_JS = None
self.SLIDES_JS = None
self.slides = []
self.title = None
self.mp4_url = None
def __init__(self, server=None, port='80'):
self.reset()
self.server = server
self.port = port
def all_checked(self):
for flag in self.checked:
if not self.checked[flag]:
return False
return True
def make_urls(self):
urls = ''
if self.mp4_url is not None:
urls += self.mp4_url
for slide in self.slides:
urls += '\n'+slide
if urls.startswith('\n'):
urls = urls[1:]
return urls
def make_player(self, name='player.html'):
player_str = '''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
<style>
body{color:#000;font:1em Lucida, Lucida Grande, Tahoma, sans-serif;width:930px;margin-left:auto;margin-right:auto;}
#steps{width:100%;padding-top:5px}
#playerContainer{float: left; margin-right: 0px;}
#slideArea{padding: 0; width: 600px; height: 365px; float: left; margin-left: 6px;}
#slideContainer{width:100%; height:100%; text-align:center;}
#slide{visibility:visible;width:100%; height:100%;}
#urls{margin-top:10px;margin-bottom:5px;width:920px;display:none;}
span.link{color:blue;}
span.link:hover {cursor:pointer;color:darkred;}
</style>
<script type="text/javascript">
var prevIdx = -1;
${SLIDES}
${TIMES}
function init(){
var player=document.getElementById('player');
if (player && player.canPlayType && player.canPlayType('video/mp4').replace(/no/, '')) {
player.addEventListener('timeupdate', function(e){
for (var idx=0; idx<TIMES.length-1;idx++){
if (player.currentTime>=TIMES[idx] && player.currentTime<TIMES[idx+1]){
if (prevIdx != idx) {
prevIdx = idx;
var slide = document.getElementById('slideImg')
if (slide){
slide.setAttribute('src', slides[idx]);
}
}
break;
}
}
});
};
};
function selectURLs(){
var urls = document.getElementById('urls');
if (urls){
if (urls.style.display == "none" || urls.style.display == ""){
urls.style.display = "block";
urls.focus();
urls.select();
} else {
urls.style.display = "none";
}
}
};
</script>
</head>
<body onload="init();">
<div id="steps">
<a href="${url}" target="_blank">${title}</a>
<p>
<b>${HOWTO}</b>
<div>${copy}<span class="link" onclick="selectURLs();">${urls_str}</span>${step1}</div>
<textarea id="urls" rows=5>${urls}</textarea>
<div>${step2}</div>
<div style="margin-bottom:15px;">${step3}</div>
</div>
<div id="playerContainer">
<video id="player" width="320" height="265" autoplay controls poster=${infoq_logo}>
<source id="source" src="${mp4}" type="video/mp4" />
${hints}
</video>
</div>
<div id="slideArea">
<div id="slideContainer"><div id="slide">
<img id="slideImg" style="height:100%;width:100%;" src="">
</div></div>
</div>
<script type="text/javascript">
var slide = document.getElementById('slideImg')
if (slide){
slide.setAttribute('src', slides[0]);
}
</script>
</body>
</html>'''
if self.mp4_url is not None and len(self.slides)>0:
VIDEO_RE = re.compile(r'/.*/([a-zA-z0-9].*\.mp4)$')
result = VIDEO_RE.search(self.mp4_url)
if result:
try:
file = codecs.open(os.curdir + os.sep + name, 'w', encoding='utf-8')
player_dict = dict(url=unicode(self.url,'utf-8'), \
title=unicode(self.title,'utf-8'), \
HOWTO=_('HOWTO'), \
SLIDES=unicode(self.SLIDES_JS,'utf-8'), \
TIMES=unicode(self.TIMES_JS,'utf-8'), \
copy=_('1. copy the '), \
urls_str=_('urls'), \
step1=_(', then download them all to a directory.'), \
step2=_('2. copy player.html to the same directory.'), \
step3=_('3. browse player.html with Chrome, Safari, or IE 9+.'), \
hints=_('Your browser does not support HTML5 video. <br>Please use Chrome, Safari, or IE 9+.'), \
mp4=unicode(result.group(1),'utf-8'), \
urls=unicode(self.make_urls(),'utf-8'), \
infoq_logo=INFOQ_PNG_BASE64)
file.write(Template(player_str).substitute(player_dict))
file.close()
except (ValueError,IOError), (err):
print >> sys.stderr, err
def parse(self, url):
file = netlib.urlfetch(url, ua=netlib.IPAD_UA, server=self.server, port=self.port)
if file is not None:
self.reset()
self.url = url
try:
for line in file:
if not self.checked['title']:
result = Presentation.TITLE_RE.search(line)
if result:
self.title = result.group(1)
self.checked['title'] = True
if self.all_checked():
break
else:
continue
if not self.checked['slides']:
result = Presentation.SLIDES_RE.search(line)
if result:
slides = result.group(1).split(',')
slide_re = re.compile(r'\'(.*)\'')
slide_name_re = re.compile(r'/.*/([a-zA-z0-9].*\.jpg)$')
local_slides = []
for idx in range(len(slides)):
result = slide_re.match(slides[idx])
if result:
slides[idx] = 'http://www.infoq.com' + result.group(1)
self.slides.append(slides[idx])
result = slide_name_re.search(slides[idx])
if result:
local_slides.append("'" + result.group(1) + "'")
self.SLIDES_JS = 'var slides= new Array(' + ','.join(local_slides) + ');'
self.checked['slides'] = True
if self.all_checked():
break
else:
continue
if not self.checked['TIMES']:
result = Presentation.TIMES_RE.search(line)
if result:
self.TIMES_JS = result.group(0)
self.TIMES_JS = 'var '+self.TIMES_JS+';'
self.checked['TIMES'] = True
if self.all_checked():
break
else:
continue
if not self.checked['mp4']:
result = Presentation.MP4_RE.search(line)
if result:
self.mp4_url = result.group(1)
self.checked['mp4'] = True
if self.all_checked():
break
else:
continue
except (urllib2.URLError, socket.error, socket.herror, socket.gaierror, socket.timeout), (err):
print >> sys.stderr, err
#output
if self.mp4_url is not None:
print >> sys.stdout, self.mp4_url
else:
print >> sys.stderr, _('NO mp4 found.')
for slide in self.slides:
print >> sys.stdout, slide
self.make_player()
### END class Presentation ###