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