Skip to content

Commit cbb67f3

Browse files
committed
Implement Audio widget
1 parent 326057e commit cbb67f3

File tree

7 files changed

+186
-58
lines changed

7 files changed

+186
-58
lines changed

docs/source/examples/Media widgets.ipynb

Lines changed: 18 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,25 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": 6,
5+
"execution_count": null,
66
"metadata": {},
77
"outputs": [],
88
"source": [
9-
"from ipywidgets import Image, Video#, Audio"
9+
"from ipywidgets import Image, Video, Audio"
1010
]
1111
},
1212
{
1313
"cell_type": "code",
14-
"execution_count": 7,
14+
"execution_count": null,
1515
"metadata": {},
16-
"outputs": [
17-
{
18-
"data": {
19-
"application/vnd.jupyter.widget-view+json": {
20-
"model_id": "c27a5a6edd394f919adff1e62ba80b68",
21-
"version_major": 2,
22-
"version_minor": 0
23-
},
24-
"text/plain": [
25-
"Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x012\\x00\\x00\\x01\\xb1\\x08\\x06\\x00\\x00\\x00\\xe1\\xe2:\\xb…"
26-
]
27-
},
28-
"metadata": {},
29-
"output_type": "display_data"
30-
}
31-
],
16+
"outputs": [],
3217
"source": [
3318
"Image.from_file(\"images/WidgetArch.png\")"
3419
]
3520
},
3621
{
3722
"cell_type": "code",
38-
"execution_count": 2,
23+
"execution_count": null,
3924
"metadata": {},
4025
"outputs": [],
4126
"source": [
@@ -44,24 +29,9 @@
4429
},
4530
{
4631
"cell_type": "code",
47-
"execution_count": 3,
32+
"execution_count": null,
4833
"metadata": {},
49-
"outputs": [
50-
{
51-
"data": {
52-
"application/vnd.jupyter.widget-view+json": {
53-
"model_id": "3350485f6bf94b1f8d5faee0f8e2c0a7",
54-
"version_major": 2,
55-
"version_minor": 0
56-
},
57-
"text/plain": [
58-
"Video(value=b'\\x00\\x00\\x00\\x18ftypmp42\\x00\\x00\\x00\\x00isommp42\\x00\\x00+\\xadmoov\\x00\\x00\\x00lmvhd\\x00\\x00\\x00\\x…"
59-
]
60-
},
61-
"metadata": {},
62-
"output_type": "display_data"
63-
}
64-
],
34+
"outputs": [],
6535
"source": [
6636
"video1"
6737
]
@@ -77,28 +47,22 @@
7747
},
7848
{
7949
"cell_type": "code",
80-
"execution_count": 4,
50+
"execution_count": null,
8151
"metadata": {},
82-
"outputs": [
83-
{
84-
"data": {
85-
"application/vnd.jupyter.widget-view+json": {
86-
"model_id": "eb271218c80942bb8a85f9cb7a8a24f4",
87-
"version_major": 2,
88-
"version_minor": 0
89-
},
90-
"text/plain": [
91-
"Video(value=b'https://webrtc.github.io/samples/src/video/chrome.webm', format='url')"
92-
]
93-
},
94-
"metadata": {},
95-
"output_type": "display_data"
96-
}
97-
],
52+
"outputs": [],
9853
"source": [
9954
"video2 = Video.from_url(\"https://webrtc.github.io/samples/src/video/chrome.webm\", autoplay=False)\n",
10055
"video2"
10156
]
57+
},
58+
{
59+
"cell_type": "code",
60+
"execution_count": null,
61+
"metadata": {},
62+
"outputs": [],
63+
"source": [
64+
"Audio.from_file(\"images/Big.Buck.Bunny.mp3\", controls=True)"
65+
]
10266
}
10367
],
10468
"metadata": {
518 KB
Binary file not shown.

ipywidgets/widgets/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@
2323
from .interaction import interact, interactive, fixed, interact_manual, interactive_output
2424
from .widget_link import jslink, jsdlink
2525
from .widget_layout import Layout
26-
from .widget_media import Image, Video
26+
from .widget_media import Image, Video, Audio
2727
from .widget_style import Style

ipywidgets/widgets/widget_media.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,32 @@ def from_file(cls, filename, **kwargs):
199199

200200
def __repr__(self):
201201
return self._get_repr(Video)
202+
203+
204+
@register
205+
class Audio(_Media):
206+
"""Displays a audio as a widget.
207+
208+
The `value` of this widget accepts a byte string. The byte string is the
209+
raw audio data that you want the browser to display. You can explicitly
210+
define the format of the byte string using the `format` trait (which
211+
defaults to "mp3").
212+
213+
If you pass `"url"` to the `"format"` trait, `value` will be interpreted
214+
as a URL as bytes encoded in UTF-8.
215+
"""
216+
_view_name = Unicode('AudioView').tag(sync=True)
217+
_model_name = Unicode('AudioModel').tag(sync=True)
218+
219+
# Define the custom state properties to sync with the front-end
220+
format = Unicode('mp3', help="The format of the audio.").tag(sync=True)
221+
autoplay = Bool(True, help="When true, the audio starts when it's displayed").tag(sync=True)
222+
loop = Bool(True, help="When true, the audio will start from the beginning after finishing").tag(sync=True)
223+
controls = Bool(True, help="Specifies that audio controls should be displayed (such as a play/pause button etc)").tag(sync=True)
224+
225+
@classmethod
226+
def from_file(cls, filename, **kwargs):
227+
return cls._from_file('audio', filename, **kwargs)
228+
229+
def __repr__(self):
230+
return self._get_repr(Audio)

packages/controls/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export * from './widget_bool';
88
export * from './widget_button';
99
export * from './widget_box';
1010
export * from './widget_image';
11+
export * from './widget_video';
12+
export * from './widget_audio';
1113
export * from './widget_color';
1214
export * from './widget_date';
1315
export * from './widget_int';
@@ -16,7 +18,6 @@ export * from './widget_controller';
1618
export * from './widget_selection';
1719
export * from './widget_selectioncontainer';
1820
export * from './widget_string';
19-
export * from './widget_video';
2021
export * from './widget_description';
2122

2223
export

packages/controls/src/widget_audio.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (c) Jupyter Development Team.
2+
// Distributed under the terms of the Modified BSD License.
3+
4+
import {
5+
DOMWidgetView
6+
} from '@jupyter-widgets/base';
7+
8+
import {
9+
CoreDOMWidgetModel
10+
} from './widget_core';
11+
12+
import * as _ from 'underscore';
13+
14+
export
15+
class AudioModel extends CoreDOMWidgetModel {
16+
defaults() {
17+
return _.extend(super.defaults(), {
18+
_model_name: 'AudioModel',
19+
_view_name: 'AudioView',
20+
format: 'mp3',
21+
autoplay: true,
22+
loop: true,
23+
controls: true,
24+
value: new DataView(new ArrayBuffer(0))
25+
});
26+
}
27+
28+
static serializers = {
29+
...CoreDOMWidgetModel.serializers,
30+
value: {serialize: (value, manager) => {
31+
return new DataView(value.buffer.slice(0));
32+
}}
33+
};
34+
}
35+
36+
export
37+
class AudioView extends DOMWidgetView {
38+
render() {
39+
/**
40+
* Called when view is rendered.
41+
*/
42+
super.render();
43+
this.pWidget.addClass('jupyter-widgets');
44+
this.update(); // Set defaults.
45+
}
46+
47+
update() {
48+
/**
49+
* Update the contents of this view
50+
*
51+
* Called when the model is changed. The model may have been
52+
* changed by another view or by a state update from the back-end.
53+
*/
54+
55+
let url;
56+
let format = this.model.get('format');
57+
let value = this.model.get('value');
58+
if (format !== 'url') {
59+
let blob = new Blob([value], {type: `audio/${this.model.get('format')}`});
60+
url = URL.createObjectURL(blob);
61+
} else {
62+
url = (new TextDecoder('utf-8')).decode(value.buffer);
63+
}
64+
65+
// Clean up the old objectURL
66+
let oldurl = this.el.src;
67+
this.el.src = url;
68+
if (oldurl && typeof oldurl !== 'string') {
69+
URL.revokeObjectURL(oldurl);
70+
}
71+
72+
// Audio attributes
73+
this.el.loop = this.model.get('loop');
74+
this.el.autoplay = this.model.get('autoplay');
75+
this.el.controls = this.model.get('controls');
76+
77+
return super.update();
78+
}
79+
80+
remove() {
81+
if (this.el.src) {
82+
URL.revokeObjectURL(this.el.src);
83+
}
84+
super.remove();
85+
}
86+
87+
/**
88+
* The default tag name.
89+
*
90+
* #### Notes
91+
* This is a read-only attribute.
92+
*/
93+
get tagName() {
94+
// We can't make this an attribute with a default value
95+
// since it would be set after it is needed in the
96+
// constructor.
97+
return 'audio';
98+
}
99+
100+
101+
el: HTMLAudioElement;
102+
}

packages/schema/jupyterwidgetmodels.latest.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ Attribute | Type | Default | Help
7272
`layout` | reference to Layout widget | reference to new instance |
7373
`selected_index` | `null` or number (integer) | `0` | The index of the selected page. This is either an integer selecting a particular sub-widget, or None to have no widgets selected.
7474

75+
### AudioModel (@jupyter-widgets/controls, 1.3.0); AudioView (@jupyter-widgets/controls, 1.3.0)
76+
77+
Attribute | Type | Default | Help
78+
-----------------|------------------|------------------|----
79+
`_dom_classes` | array of string | `[]` | CSS classes applied to widget DOM element
80+
`_model_module` | string | `'@jupyter-widgets/controls'` |
81+
`_model_module_version` | string | `'1.3.0'` |
82+
`_model_name` | string | `'AudioModel'` |
83+
`_view_module` | string | `'@jupyter-widgets/controls'` |
84+
`_view_module_version` | string | `'1.3.0'` |
85+
`_view_name` | string | `'AudioView'` |
86+
`autoplay` | boolean | `true` | When true, the audio starts when it's displayed
87+
`controls` | boolean | `true` | Specifies that audio controls should be displayed (such as a play/pause button etc)
88+
`format` | string | `'mp3'` | The format of the audio.
89+
`layout` | reference to Layout widget | reference to new instance |
90+
`loop` | boolean | `true` | When true, the audio will start from the beginning after finishing
91+
`value` | Bytes | `b''` | The media data as a byte string.
92+
7593
### BoundedFloatTextModel (@jupyter-widgets/controls, 1.3.0); FloatTextView (@jupyter-widgets/controls, 1.3.0)
7694

7795
Attribute | Type | Default | Help
@@ -250,6 +268,20 @@ Attribute | Type | Default | Help
250268
`name` | string | `''` | The name of the controller.
251269
`timestamp` | number (float) | `0.0` | The last time the data from this gamepad was updated.
252270

271+
### DOMWidgetModel (@jupyter-widgets/controls, 1.3.0); None (@jupyter-widgets/controls, 1.3.0)
272+
273+
Attribute | Type | Default | Help
274+
-----------------|------------------|------------------|----
275+
`_dom_classes` | array of string | `[]` | CSS classes applied to widget DOM element
276+
`_model_module` | string | `'@jupyter-widgets/controls'` |
277+
`_model_module_version` | string | `'1.3.0'` |
278+
`_model_name` | string | `'DOMWidgetModel'` |
279+
`_view_module` | string | `'@jupyter-widgets/controls'` |
280+
`_view_module_version` | string | `'1.3.0'` |
281+
`_view_name` | `null` or string | `null` | Name of the view.
282+
`layout` | reference to Layout widget | reference to new instance |
283+
`value` | Bytes | `b''` | The media data as a byte string.
284+
253285
### DatePickerModel (@jupyter-widgets/controls, 1.3.0); DatePickerView (@jupyter-widgets/controls, 1.3.0)
254286

255287
Attribute | Type | Default | Help
@@ -509,7 +541,7 @@ Attribute | Type | Default | Help
509541
`format` | string | `'png'` | The format of the image.
510542
`height` | string | `''` | Height of the image in pixels.
511543
`layout` | reference to Layout widget | reference to new instance |
512-
`value` | Bytes | `b''` | The image data as a byte string.
544+
`value` | Bytes | `b''` | The media data as a byte string.
513545
`width` | string | `''` | Width of the image in pixels.
514546

515547
### IntProgressModel (@jupyter-widgets/controls, 1.3.0); ProgressView (@jupyter-widgets/controls, 1.3.0)
@@ -974,7 +1006,7 @@ Attribute | Type | Default | Help
9741006
`height` | string | `''` | Height of the video in pixels.
9751007
`layout` | reference to Layout widget | reference to new instance |
9761008
`loop` | boolean | `true` | When true, the video will start from the beginning after finishing
977-
`value` | Bytes | `b''` | The video data as a byte string.
1009+
`value` | Bytes | `b''` | The media data as a byte string.
9781010
`width` | string | `''` | Width of the video in pixels.
9791011

9801012
### OutputModel (@jupyter-widgets/output, 1.0.0); OutputView (@jupyter-widgets/output, 1.0.0)

0 commit comments

Comments
 (0)