A Django and ES6 Javascript video player app.
-
Lightweight, customisable wrapper around the video API;
-
Works around Chrome bugs where in certain circumstances requesting the video can result in a pending request which never resolves (see this bug report for example). One telltale sign of this is a "Waiting for available socket" message;
-
Sizes the video player based on the poster image, avoiding difficulties with determining video aspect ratio in responsive layouts;
-
Provides abstract models and a (Django or Jinja2 compatible) template snippet for an HTML5 video element and its associated sources; and
-
Supports a mobile version of each video source.
- Python 2.7, 3.4 or 3.5
- Django 1.8+
- Modernizr (setclasses, touchevents, pointerevents). [Download minimal build]
- 'no-js' class on the html element. [Modernizr docs]
-
pip install -e [email protected]:pbright/django-videoplayer.git#egg=django-videoplayer
-
Ensure that the
TEMPLATES
setting includes an entry with'APP_DIRS': True
, or copytemplates/videoplayer/videoplayer.html
to a location where django.template.loader.get_template can find it. -
TODO: Webpack
-
TODO: Static files
Import AbstractVideo
and create a child model class. The child class must
implement render_video
, which will typically call the parent's render_video
method passing it markup for a poster image via the poster_markup
keyword
argument. The size and aspect ratio of the video element is determined by the
poster image.
The child model class can either define a source
(and optionally a
mobile_source
) field, or a sources
reverse lookup where the related model defines a source
(and optionally a mobile_source
) field. The later case
is provided to support the video element having multiple source elements, and
the abstract class AbstractVideoSource
is provided for this purpose.
AbstractVideo provides:
- Defaults for the following video element properties, which can be
overridden by a field or attribute of the same name on the child class:
autoplay
,loop
,controls
,muted
- A
render_video
method. See templating section below.
Example minimal implementation supporting only a single source:
from videoplayer.models import \
AbstractVideo, validate_video_type, get_file_type
class TestVideo(AbstractVideo):
img_height = models.PositiveSmallIntegerField(null=True, blank=True,
editable=False)
img_width = models.PositiveSmallIntegerField(null=True, blank=True,
editable=False)
poster_image = models.ImageField(upload_to=settings.UPLOAD_PATH,
width_field='img_width',
height_field='img_height')
source = models.FileField(upload_to=settings.UPLOAD_PATH,
validators=[validate_video_type])
mobile_source = models.FileField(upload_to=settings.UPLOAD_PATH,
validators=[validate_video_type],
blank=True, null=True)
def render_video(self, **kwargs):
poster_markup = '<img src="%s" height="%d" width="%d" />' % (
self.poster_image.url, self.img_height, self.img_width)
return super(TestVideo, self).render_video(
poster_markup=poster_markup)
def __str__(self):
return str(self.source)
Example implementation overriding some defaults and supporting multiple sources:
from videoplayer.models import AbstractVideo, AbstractVideoSource
class TestVideo(AbstractVideo):
img_height = models.PositiveSmallIntegerField(null=True, blank=True,
editable=False)
img_width = models.PositiveSmallIntegerField(null=True, blank=True,
editable=False)
poster_image = models.ImageField(upload_to=settings.UPLOAD_PATH,
width_field='img_width',
height_field='img_height')
autoplay = models.BooleanField(default=True)
loop = models.BooleanField(default=True)
@property
def muted(self):
return not self.autoplay
@property
def controls(self):
if self.autoplay and self.loop:
return False
return True
def render_video(self, **kwargs):
poster_markup = '<img src="%s" height="%d" width="%d" />' % (
self.poster_image.url, self.img_height, self.img_width)
return super(TestVideo, self).render_video(
poster_markup=poster_markup)
def __str__(self):
return str(self.sources.first())
class VideoSource(AbstractVideoSource):
video = models.ForeignKey(Video, related_name='sources')
To render the video, simple call render_video
in your template.
Django example:
{{ video.render_video }}
Jinja2 example:
{{ video.render_video() }}
Lastly, amend your Javascript to instantiate a new VideoPlayer for each element with the class .video-player
.
For example:
import {VideoPlayer} from './videoplayer/videoplayer';
for (let playerElement of document.body.getElementsByClassName('video-player')) {
new VideoPlayer(playerElement);
}
TODO: replaceSrc and teardown explanation
TODO: Mobile style explanation, including hover
TODO
Colors can be defined by the following variables:
$video-background
(defaults to #000
)
$video-controls-background
(defaults to transparent
)
$video-controls-color
(defaults to #fff
)
$video-controls-active-color
(defaults to transparentize(#fff, .2)
)
$video-progress-track-background
(defaults to $video-background
)
$video-progress-thumb-color
(defaults to $video-controls-color)
Responsive breakpoints can be defined by the following variables:
$tablet-max
(defaults to 1280px
)
$phone-max
(defaults to 667px
)
A number of mixins are defined in videoplayer.scss
which can also be
overridden on a case by case basis.