##// END OF EJS Templates
Change to encodestring to b64encode to avoid newlines in the data URI...
David Österberg -
Show More
@@ -1,487 +1,489 b''
1 1 """Various display related classes.
2 2
3 3 Authors : MinRK, gregcaporaso, dannystaple
4 4 """
5 5 from os.path import exists, isfile, splitext, abspath, join, isdir
6 6 from os import walk, sep
7 7
8 8 from IPython.core.display import DisplayObject
9 9
10 10
11 11 class Audio(DisplayObject):
12 12 """Create an audio object.
13 13
14 14 When this object is returned by an input cell or passed to the
15 15 display function, it will result in Audio controls being displayed
16 16 in the frontend (only works in the notebook).
17 17
18 18 Parameters
19 19 ----------
20 20 data : numpy array, unicode, str or bytes
21 21 Can be a
22 22 * Numpy array containing the desired waveform,
23 23 * String containing the filename
24 24 * Bytestring containing raw PCM data or
25 25 * URL pointing to a file on the web.
26 26
27 27 If the array option is used the waveform will be normalized.
28 28
29 29 If a filename or url is used the format support will be browser
30 30 dependent.
31 31 url : unicode
32 32 A URL to download the data from.
33 33 filename : unicode
34 34 Path to a local file to load the data from.
35 35 embed : boolean
36 36 Should the image data be embedded using a data URI (True) or should
37 37 the original source be referenced. Set this to True if you want the
38 38 audio to playable later with no internet connection in the notebook.
39 39
40 40 Default is `True`, unless the keyword argument `url` is set, then
41 41 default value is `False`.
42 42 rate : integer
43 43 The sampling rate of the raw data.
44 44 Only required when data parameter is being used as an array
45 45 autoplay : bool
46 46 Set to True if the audio should immediately start playing.
47 47 Default is `False`.
48 48
49 49 Examples
50 50 --------
51 51
52 52 # Generate a sound
53 53 import numpy as np
54 54 framerate = 44100
55 55 t = np.linspace(0,5,framerate*5)
56 56 data = np.sin(2*np.pi*440*np.sin(10*t**2))
57 57 Audio(data,rate=framerate)
58 58
59 59 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav")
60 60 Audio(url="http://media.bradsucks.net/albums/ooi-128/01_-_Brad_Sucks_-_Dropping_out_of_School.mp3")
61 61 Audio(url="http://www.w3schools.com/html/horse.ogg", embed=True)
62 62
63 63 Audio('/path/to/sound.wav')
64 64 Audio(filename='/path/to/sound.ogg')
65 65
66 66 Audio(b'RAW_WAV_DATA..)
67 67 Audio(data=b'RAW_WAV_DATA..)
68 68
69 69 """
70 70
71 71 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False):
72 72 if filename is None and url is None and data is None:
73 73 raise ValueError("No image data found. Expecting filename, url, or data.")
74 74 if embed is False and url is None:
75 75 raise ValueError("No url found. Expecting url when embed=False")
76 76
77 77 if url is not None and embed is not True:
78 78 self.embed = False
79 79 else:
80 80 self.embed = True
81 81 self.autoplay = autoplay
82 82 super(Audio, self).__init__(data=data, url=url, filename=filename)
83 83
84 84 if self.data is not None and not isinstance(self.data, bytes):
85 85 self.data = self._make_wav(data,rate)
86 86
87 87 def reload(self):
88 88 """Reload the raw data from file or URL."""
89 89 import mimetypes
90 90 if self.embed:
91 91 super(Audio, self).reload()
92 92
93 93 if self.filename is not None:
94 94 self.mimetype = mimetypes.guess_type(self.filename)[0]
95 95 elif self.url is not None:
96 96 self.mimetype = mimetypes.guess_type(self.url)[0]
97 97 else:
98 98 self.mimetype = "audio/wav"
99 99
100 100
101 101 def _make_wav(self,data,rate):
102 102 """ Transform a numpy array to a PCM bytestring """
103 103 import struct
104 104 from io import BytesIO
105 105 import wave
106 106 maxabsvalue = max(map(abs,data))
107 107 scaled = map(lambda x: int(x/maxabsvalue*32767), data)
108 108 fp = BytesIO()
109 109 waveobj = wave.open(fp,mode='wb')
110 110 waveobj.setnchannels(1)
111 111 waveobj.setframerate(rate)
112 112 waveobj.setsampwidth(2)
113 113 waveobj.setcomptype('NONE','NONE')
114 114 waveobj.writeframes(b''.join([struct.pack('<h',x) for x in scaled]))
115 115 val = fp.getvalue()
116 116 waveobj.close()
117 117 return val
118 118
119 119 def _data_and_metadata(self):
120 120 """shortcut for returning metadata with url information, if defined"""
121 121 md = {}
122 122 if self.url:
123 123 md['url'] = self.url
124 124 if md:
125 125 return self.data, md
126 126 else:
127 127 return self.data
128 128
129 129 def _repr_html_(self):
130 130 src = """
131 131 <audio controls="controls" {autoplay}>
132 132 <source src="{src}" type="{type}" />
133 133 Your browser does not support the audio element.
134 134 </audio>
135 135 """
136 136 return src.format(src=self.src_attr(),type=self.mimetype, autoplay=self.autoplay_attr())
137 137
138 138 def src_attr(self):
139 139 import base64
140 140 if self.embed and (self.data is not None):
141 return """data:{type};base64,{base64}""".format(type=self.mimetype, base64=base64.encodestring(self.data))
141 data = base64=base64.b64encode(self.data).decode('ascii'))
142 return """data:{type};base64,{base64}""".format(type=self.mimetype,
143 base64=data)
142 144 elif self.url is not None:
143 145 return self.url
144 146 else:
145 147 return ""
146 148
147 149 def autoplay_attr(self):
148 150 if(self.autoplay):
149 151 return 'autoplay="autoplay"'
150 152 else:
151 153 return ''
152 154
153 155 class IFrame(object):
154 156 """
155 157 Generic class to embed an iframe in an IPython notebook
156 158 """
157 159
158 160 iframe = """
159 161 <iframe
160 162 width="{width}"
161 163 height={height}"
162 164 src="{src}{params}"
163 165 frameborder="0"
164 166 allowfullscreen
165 167 ></iframe>
166 168 """
167 169
168 170 def __init__(self, src, width, height, **kwargs):
169 171 self.src = src
170 172 self.width = width
171 173 self.height = height
172 174 self.params = kwargs
173 175
174 176 def _repr_html_(self):
175 177 """return the embed iframe"""
176 178 if self.params:
177 179 from urllib import urlencode
178 180 params = "?" + urlencode(self.params)
179 181 else:
180 182 params = ""
181 183 return self.iframe.format(src=self.src,
182 184 width=self.width,
183 185 height=self.height,
184 186 params=params)
185 187
186 188 class YouTubeVideo(IFrame):
187 189 """Class for embedding a YouTube Video in an IPython session, based on its video id.
188 190
189 191 e.g. to embed the video on this page:
190 192
191 193 http://www.youtube.com/watch?v=foo
192 194
193 195 you would do:
194 196
195 197 vid = YouTubeVideo("foo")
196 198 display(vid)
197 199
198 200 To start from 30 seconds:
199 201
200 202 vid = YouTubeVideo("abc", start=30)
201 203 display(vid)
202 204
203 205 To calculate seconds from time as hours, minutes, seconds use:
204 206 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
205 207
206 208 Other parameters can be provided as documented at
207 209 https://developers.google.com/youtube/player_parameters#parameter-subheader
208 210 """
209 211
210 212 def __init__(self, id, width=400, height=300, **kwargs):
211 213 src = "http://www.youtube.com/embed/{0}".format(id)
212 214 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
213 215
214 216 class VimeoVideo(IFrame):
215 217 """
216 218 Class for embedding a Vimeo video in an IPython session, based on its video id.
217 219 """
218 220
219 221 def __init__(self, id, width=400, height=300, **kwargs):
220 222 src="http://player.vimeo.com/video/{0}".format(id)
221 223 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
222 224
223 225 class ScribdDocument(IFrame):
224 226 """
225 227 Class for embedding a Scribd document in an IPython session
226 228
227 229 Use the start_page params to specify a starting point in the document
228 230 Use the view_mode params to specify display type one off scroll | slideshow | book
229 231
230 232 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
231 233
232 234 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
233 235 """
234 236
235 237 def __init__(self, id, width=400, height=300, **kwargs):
236 238 src="http://www.scribd.com/embeds/{0}/content".format(id)
237 239 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
238 240
239 241 class FileLink(object):
240 242 """Class for embedding a local file link in an IPython session, based on path
241 243
242 244 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
243 245
244 246 you would do::
245 247
246 248 local_file = FileLink("my/data.txt")
247 249 display(local_file)
248 250
249 251 or in the HTML notebook, just::
250 252
251 253 FileLink("my/data.txt")
252 254 """
253 255
254 256 html_link_str = "<a href='%s' target='_blank'>%s</a>"
255 257
256 258 def __init__(self,
257 259 path,
258 260 url_prefix='files/',
259 261 result_html_prefix='',
260 262 result_html_suffix='<br>'):
261 263 """
262 264 Parameters
263 265 ----------
264 266 path : str
265 267 path to the file or directory that should be formatted
266 268 directory_prefix : str
267 269 prefix to be prepended to all files to form a working link [default:
268 270 'files']
269 271 result_html_prefix : str
270 272 text to append to beginning to link [default: none]
271 273 result_html_suffix : str
272 274 text to append at the end of link [default: '<br>']
273 275 """
274 276 if isdir(path):
275 277 raise ValueError,\
276 278 ("Cannot display a directory using FileLink. "
277 279 "Use FileLinks to display '%s'." % path)
278 280 self.path = path
279 281 self.url_prefix = url_prefix
280 282 self.result_html_prefix = result_html_prefix
281 283 self.result_html_suffix = result_html_suffix
282 284
283 285 def _format_path(self):
284 286 fp = ''.join([self.url_prefix,self.path])
285 287 return ''.join([self.result_html_prefix,
286 288 self.html_link_str % (fp, self.path),
287 289 self.result_html_suffix])
288 290
289 291 def _repr_html_(self):
290 292 """return html link to file
291 293 """
292 294 if not exists(self.path):
293 295 return ("Path (<tt>%s</tt>) doesn't exist. "
294 296 "It may still be in the process of "
295 297 "being generated, or you may have the "
296 298 "incorrect path." % self.path)
297 299
298 300 return self._format_path()
299 301
300 302 def __repr__(self):
301 303 """return absolute path to file
302 304 """
303 305 return abspath(self.path)
304 306
305 307 class FileLinks(FileLink):
306 308 """Class for embedding local file links in an IPython session, based on path
307 309
308 310 e.g. to embed links to files that were generated in the IPython notebook under my/data
309 311
310 312 you would do:
311 313
312 314 local_files = FileLinks("my/data")
313 315 display(local_files)
314 316
315 317 or in the HTML notebook, just
316 318
317 319 FileLinks("my/data")
318 320
319 321 """
320 322 def __init__(self,
321 323 path,
322 324 url_prefix='files/',
323 325 included_suffixes=None,
324 326 result_html_prefix='',
325 327 result_html_suffix='<br>',
326 328 notebook_display_formatter=None,
327 329 terminal_display_formatter=None):
328 330 """
329 331 included_suffixes : list of filename suffixes to include when
330 332 formatting output [default: include all files]
331 333
332 334 See the FileLink (baseclass of LocalDirectory) docstring for
333 335 information on additional parameters.
334 336
335 337 notebook_display_formatter : func used to format links for display
336 338 in the notebook. See discussion of formatter function below.
337 339
338 340 terminal_display_formatter : func used to format links for display
339 341 in the terminal. See discussion of formatter function below.
340 342
341 343
342 344 Passing custom formatter functions
343 345 ----------------------------------
344 346 Formatter functions must be of the form:
345 347 f(dirname, fnames, included_suffixes)
346 348 dirname : the name of a directory (a string),
347 349 fnames : a list of the files in that directory
348 350 included_suffixes : a list of the file suffixes that should be
349 351 included in the output (passing None means
350 352 to include all suffixes in the output in
351 353 the built-in formatters)
352 354
353 355 returns a list of lines that should will be print in the
354 356 notebook (if passing notebook_display_formatter) or the terminal
355 357 (if passing terminal_display_formatter). This function is iterated
356 358 over for each directory in self.path. Default formatters are in
357 359 place, can be passed here to support alternative formatting.
358 360
359 361 """
360 362 if isfile(path):
361 363 raise ValueError,\
362 364 ("Cannot display a file using FileLinks. "
363 365 "Use FileLink to display '%s'." % path)
364 366 self.included_suffixes = included_suffixes
365 367 # remove trailing slashs for more consistent output formatting
366 368 path = path.rstrip('/')
367 369
368 370 self.path = path
369 371 self.url_prefix = url_prefix
370 372 self.result_html_prefix = result_html_prefix
371 373 self.result_html_suffix = result_html_suffix
372 374
373 375 self.notebook_display_formatter = \
374 376 notebook_display_formatter or self._get_notebook_display_formatter()
375 377 self.terminal_display_formatter = \
376 378 terminal_display_formatter or self._get_terminal_display_formatter()
377 379
378 380 def _get_display_formatter(self,
379 381 dirname_output_format,
380 382 fname_output_format,
381 383 fp_format,
382 384 fp_cleaner=None):
383 385 """ generate built-in formatter function
384 386
385 387 this is used to define both the notebook and terminal built-in
386 388 formatters as they only differ by some wrapper text for each entry
387 389
388 390 dirname_output_format: string to use for formatting directory
389 391 names, dirname will be substituted for a single "%s" which
390 392 must appear in this string
391 393 fname_output_format: string to use for formatting file names,
392 394 if a single "%s" appears in the string, fname will be substituted
393 395 if two "%s" appear in the string, the path to fname will be
394 396 substituted for the first and fname will be substituted for the
395 397 second
396 398 fp_format: string to use for formatting filepaths, must contain
397 399 exactly two "%s" and the dirname will be subsituted for the first
398 400 and fname will be substituted for the second
399 401 """
400 402 def f(dirname, fnames, included_suffixes=None):
401 403 result = []
402 404 # begin by figuring out which filenames, if any,
403 405 # are going to be displayed
404 406 display_fnames = []
405 407 for fname in fnames:
406 408 if (isfile(join(dirname,fname)) and
407 409 (included_suffixes == None or
408 410 splitext(fname)[1] in included_suffixes)):
409 411 display_fnames.append(fname)
410 412
411 413 if len(display_fnames) == 0:
412 414 # if there are no filenames to display, don't print anything
413 415 # (not even the directory name)
414 416 pass
415 417 else:
416 418 # otherwise print the formatted directory name followed by
417 419 # the formatted filenames
418 420 dirname_output_line = dirname_output_format % dirname
419 421 result.append(dirname_output_line)
420 422 for fname in display_fnames:
421 423 fp = fp_format % (dirname,fname)
422 424 if fp_cleaner is not None:
423 425 fp = fp_cleaner(fp)
424 426 try:
425 427 # output can include both a filepath and a filename...
426 428 fname_output_line = fname_output_format % (fp, fname)
427 429 except TypeError:
428 430 # ... or just a single filepath
429 431 fname_output_line = fname_output_format % fname
430 432 result.append(fname_output_line)
431 433 return result
432 434 return f
433 435
434 436 def _get_notebook_display_formatter(self,
435 437 spacer="&nbsp;&nbsp;"):
436 438 """ generate function to use for notebook formatting
437 439 """
438 440 dirname_output_format = \
439 441 self.result_html_prefix + "%s/" + self.result_html_suffix
440 442 fname_output_format = \
441 443 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
442 444 fp_format = self.url_prefix + '%s/%s'
443 445 if sep == "\\":
444 446 # Working on a platform where the path separator is "\", so
445 447 # must convert these to "/" for generating a URI
446 448 def fp_cleaner(fp):
447 449 # Replace all occurences of backslash ("\") with a forward
448 450 # slash ("/") - this is necessary on windows when a path is
449 451 # provided as input, but we must link to a URI
450 452 return fp.replace('\\','/')
451 453 else:
452 454 fp_cleaner = None
453 455
454 456 return self._get_display_formatter(dirname_output_format,
455 457 fname_output_format,
456 458 fp_format,
457 459 fp_cleaner)
458 460
459 461 def _get_terminal_display_formatter(self,
460 462 spacer=" "):
461 463 """ generate function to use for terminal formatting
462 464 """
463 465 dirname_output_format = "%s/"
464 466 fname_output_format = spacer + "%s"
465 467 fp_format = '%s/%s'
466 468
467 469 return self._get_display_formatter(dirname_output_format,
468 470 fname_output_format,
469 471 fp_format)
470 472
471 473 def _format_path(self):
472 474 result_lines = []
473 475 walked_dir = list(walk(self.path))
474 476 walked_dir.sort()
475 477 for dirname, subdirs, fnames in walked_dir:
476 478 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
477 479 return '\n'.join(result_lines)
478 480
479 481 def __repr__(self):
480 482 """return newline-separated absolute paths
481 483 """
482 484 result_lines = []
483 485 walked_dir = list(walk(self.path))
484 486 walked_dir.sort()
485 487 for dirname, subdirs, fnames in walked_dir:
486 488 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
487 489 return '\n'.join(result_lines)
General Comments 0
You need to be logged in to leave comments. Login now