-
-
Notifications
You must be signed in to change notification settings - Fork 477
/
media.py
184 lines (133 loc) · 5.51 KB
/
media.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
"""
Contains Media panes including renderers for Audio and Video content.
"""
from __future__ import annotations
import os
from base64 import b64encode
from io import BytesIO
from typing import ClassVar, Mapping
import numpy as np
import param
from ..models import Audio as _BkAudio, Video as _BkVideo
from ..util import isfile, isurl
from .base import PaneBase
class _MediaBase(PaneBase):
loop = param.Boolean(default=False, doc="""
Whether the meida should loop""")
time = param.Number(default=0, doc="""
The current timestamp""")
throttle = param.Integer(default=250, doc="""
How frequently to sample the current playback time in milliseconds""")
paused = param.Boolean(default=True, doc="""
Whether the media is currently paused""")
object = param.String(default='', allow_None=True, doc="""
The media file either local or remote.""")
volume = param.Number(default=None, bounds=(0, 100), doc="""
The volume of the media player.""")
autoplay = param.Boolean(default=False, doc="""
When True, it specifies that the output will play automatically.
In Chromium browsers this requires the user to click play once.""")
muted = param.Boolean(default=False, doc="""
When True, it specifies that the output should be muted.""")
_default_mime = None
_formats = []
_media_type = None
_rename: ClassVar[Mapping[str, str | None]] = {'name': None, 'sample_rate': None, 'object': 'value'}
_rerender_params = []
_updates = True
__abstract = True
@classmethod
def applies(cls, obj):
if isinstance(obj, str):
if isfile(obj) and any(obj.endswith('.'+fmt) for fmt in cls._formats):
return True
if isurl(obj, cls._formats):
return True
if hasattr(obj, 'read'): # Check for file like object
return True
return False
def _get_model(self, doc, root=None, parent=None, comm=None):
props = self._process_param_change(self._init_params())
model = self._bokeh_model(**props)
if root is None:
root = model
self._models[root.ref['id']] = (model, parent)
self._link_props(model, list(model.properties()), doc, root, comm)
return model
def _from_numpy(self, data):
from scipy.io import wavfile
buffer = BytesIO()
wavfile.write(buffer, self.sample_rate, data)
return buffer
def _process_property_change(self, msg):
msg = super()._process_property_change(msg)
if 'js_property_callbacks' in msg:
del msg['js_property_callbacks']
return msg
def _process_param_change(self, msg):
msg = super()._process_param_change(msg)
if 'value' in msg:
value = msg['value']
fmt = self._default_mime
if isinstance(value, np.ndarray):
fmt = 'wav'
buffer = self._from_numpy(value)
data = b64encode(buffer.getvalue())
elif os.path.isfile(value):
fmt = value.split('.')[-1]
with open(value, 'rb') as f:
data = f.read()
data = b64encode(data)
elif value.lower().startswith('http'):
return msg
elif not value or value == f'data:{self._media_type}/{fmt};base64,':
data = b''
else:
raise ValueError(f'Object should be either path to a {self._media_type} file or numpy array.')
msg['value'] = f"data:{self._media_type}/{fmt};base64,{data.decode('utf-8')}"
return msg
class Audio(_MediaBase):
"""
The `Audio` pane displays an audio player given a local or remote audio
file or numpy array.
The pane also allows access and control over the player state including
toggling of playing/paused and loop state, the current time, and the
volume.
The audio player supports ogg, mp3, and wav files as well as numpy arrays.
Reference: https://panel.holoviz.org/reference/panes/Audio.html
:Example:
>>> Audio('http://ccrma.stanford.edu/~jos/mp3/pno-cs.mp3', name='Audio')
"""
object = param.ClassSelector(default='', class_=(str, np.ndarray,),
allow_None=True, doc="""
The audio file either local or remote.""")
sample_rate = param.Integer(default=44100, doc="""
The sample_rate of the audio when given a NumPy array.""")
_bokeh_model = _BkAudio
_default_mime = 'wav'
_formats = ['mp3', 'wav', 'ogg']
_media_type = 'audio'
@classmethod
def applies(cls, obj):
return (super().applies(obj) or
(isinstance(obj, np.ndarray) and obj.ndim==1 and obj.dtype in [np.int16, np.uint16]))
class Video(_MediaBase):
"""
The `Video` Pane displays a video player given a local or remote video
file.
The widget also allows access and control over the player state including
toggling of playing/paused and loop state, the current time, and the
volume.
Depending on the browser the video player supports mp4, webm, and ogg
containers and a variety of codecs.
Reference: https://panel.holoviz.org/reference/panes/Video.html
:Example:
>>> Video(
... 'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_640_3MG.mp4',
... width=640, height=360, loop=True
... )
"""
_bokeh_model = _BkVideo
_default_mime = 'mp4'
_formats = ['mp4', 'webm', 'ogg']
_media_type = 'video'