# Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. import mimetypes from .widget_core import CoreWidget from .domwidget import DOMWidget from .valuewidget import ValueWidget from .widget import register from traitlets import Unicode, CUnicode, Bytes, Bool from .trait_types import bytes_serialization from .util import text_type @register class _Media(DOMWidget, ValueWidget, CoreWidget): """Base class for Image, Audio and Video widgets. The `value` of this widget accepts a byte string. The byte string is the raw data that you want the browser to display. If you pass `"url"` to the `"format"` trait, `value` will be interpreted as a URL as bytes encoded in UTF-8. """ # Define the custom state properties to sync with the front-end value = Bytes(help="The media data as a byte string.").tag(sync=True, **bytes_serialization) @classmethod def _from_file(cls, tag, filename, **kwargs): """ Create an :class:`Media` from a local file. Parameters ---------- filename: str The location of a file to read into the value from disk. **kwargs: The keyword arguments for `Media` Returns an `Media` with the value set from the filename. """ value = cls._load_file_value(filename) if 'format' not in kwargs: format = cls._guess_format(tag, filename) if format is not None: kwargs['format'] = format return cls(value=value, **kwargs) @classmethod def from_url(cls, url, **kwargs): """ Create an :class:`Media` from a URL. :code:`Media.from_url(url)` is equivalent to: .. code-block: python med = Media(value=url, format='url') But both unicode and bytes arguments are allowed for ``url``. Parameters ---------- url: [str, bytes] The location of a URL to load. """ if isinstance(url, text_type): # If unicode (str in Python 3), it needs to be encoded to bytes url = url.encode('utf-8') return cls(value=url, format='url') def set_value_from_file(self, filename): """ Convenience method for reading a file into `value`. Parameters ---------- filename: str The location of a file to read into value from disk. """ value = self._load_file_value(filename) self.value = value @classmethod def _load_file_value(cls, filename): if getattr(filename, 'read', None) is not None: return filename.read() else: with open(filename, 'rb') as f: return f.read() @classmethod def _guess_format(cls, tag, filename): # file objects may have a .name parameter name = getattr(filename, 'name', None) name = name or filename try: mtype, _ = mimetypes.guess_type(name) if not mtype.startswith('{}/'.format(tag)): return None return mtype[len('{}/'.format(tag)):] except Exception: return None def _get_repr(self, cls): # Truncate the value in the repr, since it will # typically be very, very large. class_name = self.__class__.__name__ # Return value first like a ValueWidget signature = [] sig_value = repr(self.value) prefix, rest = sig_value.split("'", 1) content = rest[:-1] if len(content) > 100: sig_value = "{}'{}...'".format(prefix, content[0:100]) signature.append('%s=%s' % ('value', sig_value)) for key in super(cls, self)._repr_keys(): if key == 'value': continue value = str(getattr(self, key)) signature.append('%s=%r' % (key, value)) signature = ', '.join(signature) return '%s(%s)' % (class_name, signature) @register class Image(_Media): """Displays an image as a widget. The `value` of this widget accepts a byte string. The byte string is the raw image data that you want the browser to display. You can explicitly define the format of the byte string using the `format` trait (which defaults to "png"). If you pass `"url"` to the `"format"` trait, `value` will be interpreted as a URL as bytes encoded in UTF-8. """ _view_name = Unicode('ImageView').tag(sync=True) _model_name = Unicode('ImageModel').tag(sync=True) # Define the custom state properties to sync with the front-end format = Unicode('png', help="The format of the image.").tag(sync=True) width = CUnicode(help="Width of the image in pixels. Use layout.width " "for styling the widget.").tag(sync=True) height = CUnicode(help="Height of the image in pixels. Use layout.height " "for styling the widget.").tag(sync=True) def __init__(self, *args, **kwargs): super(Image, self).__init__(*args, **kwargs) @classmethod def from_file(cls, filename, **kwargs): return cls._from_file('image', filename, **kwargs) def __repr__(self): return self._get_repr(Image) @register class Video(_Media): """Displays a video as a widget. The `value` of this widget accepts a byte string. The byte string is the raw video data that you want the browser to display. You can explicitly define the format of the byte string using the `format` trait (which defaults to "mp4"). If you pass `"url"` to the `"format"` trait, `value` will be interpreted as a URL as bytes encoded in UTF-8. """ _view_name = Unicode('VideoView').tag(sync=True) _model_name = Unicode('VideoModel').tag(sync=True) # Define the custom state properties to sync with the front-end format = Unicode('mp4', help="The format of the video.").tag(sync=True) width = CUnicode(help="Width of the video in pixels.").tag(sync=True) height = CUnicode(help="Height of the video in pixels.").tag(sync=True) autoplay = Bool(True, help="When true, the video starts when it's displayed").tag(sync=True) loop = Bool(True, help="When true, the video will start from the beginning after finishing").tag(sync=True) controls = Bool(True, help="Specifies that video controls should be displayed (such as a play/pause button etc)").tag(sync=True) @classmethod def from_file(cls, filename, **kwargs): return cls._from_file('video', filename, **kwargs) def __repr__(self): return self._get_repr(Video) @register class Audio(_Media): """Displays a audio as a widget. The `value` of this widget accepts a byte string. The byte string is the raw audio data that you want the browser to display. You can explicitly define the format of the byte string using the `format` trait (which defaults to "mp3"). If you pass `"url"` to the `"format"` trait, `value` will be interpreted as a URL as bytes encoded in UTF-8. """ _view_name = Unicode('AudioView').tag(sync=True) _model_name = Unicode('AudioModel').tag(sync=True) # Define the custom state properties to sync with the front-end format = Unicode('mp3', help="The format of the audio.").tag(sync=True) autoplay = Bool(True, help="When true, the audio starts when it's displayed").tag(sync=True) loop = Bool(True, help="When true, the audio will start from the beginning after finishing").tag(sync=True) controls = Bool(True, help="Specifies that audio controls should be displayed (such as a play/pause button etc)").tag(sync=True) @classmethod def from_file(cls, filename, **kwargs): return cls._from_file('audio', filename, **kwargs) def __repr__(self): return self._get_repr(Audio)