diff --git a/.coveragerc b/.coveragerc
index af8fbca..d5cf153 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,2 +1,2 @@
-[report]
+[run]
include = embed_video/*
diff --git a/.gitignore b/.gitignore
index 565372c..688d829 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ dist
.coverage
htmlcov
.idea
+.tox
diff --git a/.travis.yml b/.travis.yml
index de1ddf5..01fca11 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,27 +1,20 @@
-sudo: false
language: python
-python:
- - "2.7"
- - "3.4"
-cache: pip
+cache:
+ directories:
+ - $HOME/.cache/pip
env:
- - DJANGO_VERSION=1.5.2
- - DJANGO_VERSION=1.6.5
- - DJANGO_VERSION=1.7.0
- - DJANGO_VERSION=1.8.0
- - DJANGO_VERSION=1.8.6
- - DJANGO_VERSION=1.9.4
-install:
- - pip install -q Django==$DJANGO_VERSION
- - pip install coveralls
- - pip install mock
- - pip install south
- - pip install nose
- - pip install testfixtures
-script:
- - python setup.py build
- - PYTHONHASHSEED=0 python setup.py nosetests --verbosity 2 --with-coverage --cover-tests --cover-erase
-after_success:
- - coveralls
-notifications:
- email: false
+ - TOXENV=py27-django15
+ - TOXENV=py27-django16
+ - TOXENV=py27-django17
+ - TOXENV=py27-django18
+ - TOXENV=py27-django19
+ - TOXENV=py27-django110
+ - TOXENV=py34-django15
+ - TOXENV=py34-django16
+ - TOXENV=py34-django17
+ - TOXENV=py34-django18
+ - TOXENV=py34-django19
+ - TOXENV=py34-django110
+install: pip install tox coveralls
+script: tox
+after_success: coveralls
diff --git a/MANIFEST.in b/MANIFEST.in
index bdd1844..7193225 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -3,3 +3,8 @@ include CHANGES.rst
include LICENSE
recursive-include embed_video/templates *.html
+
+recursive-exclude * __pycache__
+recursive-exclude * *.py[co]
+recursive-exclude * *~
+recursive-exclude * .coverage
diff --git a/docs/development/index.rst b/docs/development/index.rst
index d875990..ad0733a 100644
--- a/docs/development/index.rst
+++ b/docs/development/index.rst
@@ -1,4 +1,4 @@
-Development
+Development
===========
diff --git a/docs/development/testing.rst b/docs/development/testing.rst
index fa21d58..342ab9e 100644
--- a/docs/development/testing.rst
+++ b/docs/development/testing.rst
@@ -4,23 +4,22 @@ Testing
Requirements
------------
-The library needs ``Django`` and ``requests`` and ``nose``, ``mock``,
-``south`` and ``testfixtures`` libraries to run tests.
-
-::
+The library needs a copy of ``Django``, e.g.::
pip install Django
- pip install requests
- pip install nose
- pip install mock
- pip install south
- pip install testfixtures
+And several libraries that can be installed from the root of
+a source checkout::
+
+ pip install -r embed_video/tests/test-requirements.txt
+
+It's recommended to make a separate virtual environment where you
+install these packages.
Running tests
-------------
-Run tests with this command:
+Run tests in your virtual environment with this command:
::
@@ -34,7 +33,13 @@ Run tests with coverage:
::
- pip install coverage
nosetests --with-coverage --cover-package=embed_video
+Use tox if you want to test against all supported Django versions,
+similar to the Travis build. Install it once::
+ pip install tox>=2.0
+
+Run the tests with::
+
+ tox
diff --git a/docs/example-project.rst b/docs/example-project.rst
index 07dd684..b1d0bb5 100644
--- a/docs/example-project.rst
+++ b/docs/example-project.rst
@@ -1,7 +1,8 @@
Example project
===============
-For easy start with using django-embed-video, you can take a look at example
-project. It is located in example_project directory in root of repository.
+To get started with django-embed-video, you can take a look at the example
+project. It is located in the ``example_project`` directory in the root of
+the repository.
.. include:: ../example_project/README.rst
diff --git a/docs/examples.rst b/docs/examples.rst
index b78e3c3..45f8968 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -48,8 +48,8 @@ Default sizes are ``tiny`` (420x315), ``small`` (480x360), ``medium`` (640x480),
{% video my_video '100% x 50%' %}
-It is possible to set backend options via parameters in template tag. It is
-useful for example to enforce HTTPS protocol or set different query appended
+It is possible to set backend options via parameters in the template tag. It is
+useful to enforce HTTPS protocol for example or to set different query appended
to url.
::
@@ -73,8 +73,8 @@ to url.
.. tip::
- You can overwrite default template of embed code located in
- ``templates/embed_video/embed_code.html`` or set own file for custom
+ You can overwrite the default template of the embed code located in
+ ``templates/embed_video/embed_code.html`` or use your own file for a custom
backend (:py:data:`~embed_video.backends.VideoBackend.template_name`).
.. versionadded:: 0.9
@@ -87,7 +87,7 @@ Model examples
.. highlight:: python
-Using the ``EmbedVideoField`` provides you validation of URLs.
+Using the ``EmbedVideoField`` provides validation of URLs.
::
@@ -121,8 +121,8 @@ Use ``AdminVideoMixin`` in ``admin.py``.
Custom backends
###############
-If you have specific needs and default backends don't suits you, you can write
-your custom backend.
+If you have specific needs and the default backend doesn't suit you,
+you can write a custom backend.
``my_project/my_app/backends.py``::
@@ -139,7 +139,7 @@ your custom backend.
template_name = 'embed_video/custombackend_embed_code.html' # added in v0.9
You can also overwrite :py:class:`~embed_video.backends.VideoBackend` methods,
-if using regular expressions isn't well enough.
+if using regular expressions isn't good enough.
``my_project/my_project/settings.py``::
@@ -155,7 +155,7 @@ if using regular expressions isn't well enough.
Low level API examples
######################
-You can get instance of :py:class:`~embed_video.backends.VideoBackend` in your
+You can get an instance of :py:class:`~embed_video.backends.VideoBackend` in your
python code thanks to :py:func:`~embed_video.backends.detect_backend`:
::
diff --git a/docs/index.rst b/docs/index.rst
index bc2a4d0..09ae73c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,12 +1,11 @@
django-embed-video's documentation
==================================
-Django app for easy embeding YouTube and Vimeo videos and music from SoundCloud.
+Django app for easy embedding YouTube and Vimeo videos and music from SoundCloud.
Repository is located on GitHub: https://github.com/jazzband/django-embed-video
-
.. toctree::
:maxdepth: 2
:glob:
@@ -15,7 +14,7 @@ Repository is located on GitHub: https://github.com/jazzband/django-embed-video
examples
example-project
- development/index
+ development/index
websites
@@ -27,7 +26,7 @@ Library API
.. toctree::
:maxdepth: 2
- api/index
+ api/index
@@ -37,4 +36,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
-
diff --git a/docs/installation.rst b/docs/installation.rst
index 6b1baa9..17ead56 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -4,14 +4,14 @@ Installation & Setup
Installation
############
-The simpliest way is to use pip to install package:
+The simpliest way is to use pip to install the package:
.. code-block:: bash
pip install django-embed-video
-If you want latest version, you may use Git. It is fresh, but unstable.
+If you want the latest version, you may use Git. It is fresh, but unstable.
.. code-block:: bash
@@ -28,12 +28,13 @@ settings.
INSTALLED_APPS = (
...
-
+
'embed_video',
)
-To detect HTTP/S you must use :py:class:`~django.core.context_processors.request`
-context processor:
+To detect HTTP/S you must add the :py:class:`~django.template.context_processors.request`
+(or :py:class:`~django.core.context_processors.request` for Django < 1.9) context
+processor:
.. code-block:: python
diff --git a/embed_video/admin.py b/embed_video/admin.py
index 88f139d..6a58ef4 100644
--- a/embed_video/admin.py
+++ b/embed_video/admin.py
@@ -14,7 +14,7 @@ class AdminVideoWidget(forms.TextInput):
.. todo::
- Django 1.6 provides better parent for this widget -
+ Django 1.6 provides a better parent for this widget -
:py:class:`django.forms.URLInput`.
"""
diff --git a/embed_video/backends.py b/embed_video/backends.py
index 6fc957a..bc2524a 100644
--- a/embed_video/backends.py
+++ b/embed_video/backends.py
@@ -19,17 +19,23 @@ from .settings import EMBED_VIDEO_BACKENDS, EMBED_VIDEO_TIMEOUT, \
class EmbedVideoException(Exception):
- """ Parental class for all embed_video exceptions """
+ """
+ Parental class for all embed_video exceptions.
+ """
pass
class VideoDoesntExistException(EmbedVideoException):
- """ Exception thrown if video doesn't exist """
+ """
+ Exception thrown if video doesn't exist.
+ """
pass
class UnknownBackendException(EmbedVideoException):
- """ Exception thrown if video backend is not recognized. """
+ """
+ Exception thrown if video backend is not recognized.
+ """
pass
@@ -53,7 +59,6 @@ def detect_backend(url):
:return: Returns recognized VideoBackend
:rtype: VideoBackend
"""
-
for backend_name in EMBED_VIDEO_BACKENDS:
backend = import_by_path(backend_name)
if backend.is_valid(url):
@@ -97,7 +102,7 @@ class VideoBackend(object):
re_detect = None
"""
- Compilede regec (:py:func:`re.compile`) to detect, if input URL is valid
+ Compiled regex (:py:func:`re.compile`) to detect, if input URL is valid
for current backend.
Example: ``re.compile(r'^http://myvideo\.com/.*')``
@@ -140,7 +145,7 @@ class VideoBackend(object):
default_query = ''
"""
- Default query string or `QueryDict` appended to url
+ Default query string or ``QueryDict`` appended to url.
:type: str
"""
@@ -154,7 +159,7 @@ class VideoBackend(object):
def __init__(self, url):
"""
- First it tries to load data from cache and if it don't succeed, run
+ First it tries to load data from cache and if it doesn't succeed, run
:py:meth:`init` and then save it to cache.
:type url: str
@@ -201,7 +206,7 @@ class VideoBackend(object):
@property
def query(self):
"""
- String transformed to QueryDict appended to url.
+ String transformed to ``QueryDict`` appended to url.
"""
return self._query
@@ -218,7 +223,7 @@ class VideoBackend(object):
def is_valid(cls, url):
"""
Class method to control if passed url is valid for current backend. By
- default it is done by :py:data:`re_detect` regex.
+ default this is done using the :py:data:`re_detect` regex.
:type url: str
"""
@@ -280,6 +285,75 @@ class VideoBackend(object):
setattr(self, key, options[key])
+class Html5Backend(VideoBackend):
+ """
+ Backend for HTML5 media.
+ """
+ def get_url(self):
+ """
+ Returns URL.
+ """
+ return self._url
+
+ def get_code(self):
+ """
+ Returns video code.
+
+ :rtype: str
+ """
+ return None
+
+ def get_thumbnail_url(self):
+ """
+ Returns thumbnail URL.
+
+ :rtype: str
+ """
+ return None
+
+
+class Html5VideoBackend(Html5Backend):
+ """
+ Backend for HTML5 video.
+ """
+ template_name = 'embed_video/html5_video.html'
+
+ @classmethod
+ def is_valid(cls, url):
+ """
+ Class method to control if passed url is valid for current backend.
+
+ :type url: str
+ """
+ try:
+ r = requests.head(url, timeout=EMBED_VIDEO_TIMEOUT)
+ except:
+ raise VideoDoesntExistException()
+
+ return r.headers.get('content-type').startswith('video')
+
+
+class Html5AudioBackend(Html5Backend):
+ """
+ Backend for HTML5 audio.
+ """
+ template_name = 'embed_video/html5_audio.html'
+
+ @classmethod
+ def is_valid(cls, url):
+ """
+ Class method to control if passed url is valid for current backend.
+
+ :type url: str
+ """
+ try:
+ r = requests.head(url, timeout=EMBED_VIDEO_TIMEOUT)
+ except:
+ raise VideoDoesntExistException()
+
+ return r.headers.get('content-type').startswith('audio')
+
+
class YoutubeBackend(VideoBackend):
"""
Backend for YouTube URLs.
diff --git a/embed_video/fields.py b/embed_video/fields.py
index 560c840..d4b8d86 100644
--- a/embed_video/fields.py
+++ b/embed_video/fields.py
@@ -33,7 +33,7 @@ class EmbedVideoField(models.URLField):
class EmbedVideoFormField(forms.URLField):
"""
- Form field for embeded video. Descendant of
+ Form field for embedded video. Descendant of
:py:class:`django.forms.URLField`
"""
diff --git a/embed_video/settings.py b/embed_video/settings.py
index f24dcdc..d715e47 100644
--- a/embed_video/settings.py
+++ b/embed_video/settings.py
@@ -6,6 +6,8 @@ EMBED_VIDEO_BACKENDS = getattr(settings, 'EMBED_VIDEO_BACKENDS', (
'embed_video.backends.VimeoBackend',
'embed_video.backends.WistiaBackend',
'embed_video.backends.SoundCloudBackend',
+ 'embed_video.backends.Html5VideoBackend',
+ 'embed_video.backends.Html5AudioBackend',
))
""" :type: tuple[str] """
diff --git a/embed_video/templates/embed_video/html5_audio.html b/embed_video/templates/embed_video/html5_audio.html
new file mode 100644
index 0000000..0000ee0
--- /dev/null
+++ b/embed_video/templates/embed_video/html5_audio.html
@@ -0,0 +1 @@
+
diff --git a/embed_video/templates/embed_video/html5_video.html b/embed_video/templates/embed_video/html5_video.html
new file mode 100644
index 0000000..988162f
--- /dev/null
+++ b/embed_video/templates/embed_video/html5_video.html
@@ -0,0 +1 @@
+
diff --git a/embed_video/tests/backends/tests_html5backend.py b/embed_video/tests/backends/tests_html5backend.py
new file mode 100644
index 0000000..bdea65f
--- /dev/null
+++ b/embed_video/tests/backends/tests_html5backend.py
@@ -0,0 +1,28 @@
+from unittest import TestCase
+
+from . import BackendTestMixin
+from embed_video import backends
+
+
+class HTML5BackendTestCase(TestCase):
+ def test_url(self):
+ url = 'https://collab-project.github.io/videojs-wavesurfer/examples/media/heres_johnny.wav'
+ backend = backends.detect_backend(url)
+ self.assertEqual(backend.get_url(), url)
+
+
+class Html5VideoBackendTestCase(BackendTestMixin, TestCase):
+ urls = (
+ ('http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4', None),
+ ('https://collab-project.github.io/videojs-wavesurfer/examples/media/example.mp4', None),
+ )
+
+ instance = backends.Html5VideoBackend
+
+
+class Html5AudioBackendTestCase(BackendTestMixin, TestCase):
+ urls = (
+ ('https://collab-project.github.io/videojs-wavesurfer/examples/media/heres_johnny.wav', None),
+ )
+
+ instance = backends.Html5AudioBackend
diff --git a/embed_video/tests/django_settings.py b/embed_video/tests/django_settings.py
index b2f7f4f..13d9a06 100644
--- a/embed_video/tests/django_settings.py
+++ b/embed_video/tests/django_settings.py
@@ -20,6 +20,8 @@ EMBED_VIDEO_BACKENDS = (
'embed_video.backends.VimeoBackend',
'embed_video.backends.WistiaBackend',
'embed_video.backends.SoundCloudBackend',
+ 'embed_video.backends.Html5VideoBackend',
+ 'embed_video.backends.Html5AudioBackend',
'embed_video.tests.backends.tests_custom_backend.CustomBackend',
)
diff --git a/embed_video/tests/test-requirements.txt b/embed_video/tests/test-requirements.txt
new file mode 100644
index 0000000..a6933ec
--- /dev/null
+++ b/embed_video/tests/test-requirements.txt
@@ -0,0 +1,7 @@
+requests
+nose
+mock
+south
+testfixtures
+coverage
+flake8
\ No newline at end of file
diff --git a/embed_video/utils.py b/embed_video/utils.py
index 7674165..501035e 100644
--- a/embed_video/utils.py
+++ b/embed_video/utils.py
@@ -6,7 +6,7 @@ from django.core.exceptions import ImproperlyConfigured
def import_by_path(dotted_path, error_prefix=''):
"""
Import a dotted module path and return the attribute/class designated by
- the last name in the path. Raise ImproperlyConfigured if something goes
+ the last name in the path. Raise ``ImproperlyConfigured`` if something goes
wrong.
.. warning::
diff --git a/example_project/README.rst b/example_project/README.rst
index 113a29d..ca3c1a9 100644
--- a/example_project/README.rst
+++ b/example_project/README.rst
@@ -8,18 +8,21 @@ Running example project
#. Create database::
- python manage.py syncdb --noinput
+ python manage.py migrate --noinput
+
+#. And superuser::
+
+ python manage.py createsuperuser
#. Run testing server::
python manage.py runserver
-#. Take a look at http://localhost:8000 . You can log in to administration with username ``admin``
- and password ``admin``.
+#. Create new posts at http://localhost:8000/admin/ and view them on http://localhost:8000.
Testing HTTPS
*************
-To test HTTPS on development server, `follow this instructions
+To test HTTPS on a development server, `follow these instructions
`_.
diff --git a/example_project/example_project/settings.py b/example_project/example_project/settings.py
index 7e153ff..ee20caf 100644
--- a/example_project/example_project/settings.py
+++ b/example_project/example_project/settings.py
@@ -1,7 +1,6 @@
# Django settings for example_project project.
DEBUG = True
-TEMPLATE_DEBUG = DEBUG
DATABASES = {
'default': {
@@ -14,24 +13,37 @@ SITE_ID = 1
SECRET_KEY = 'u%38dln@$1!7w#cxi4np504^sa3_skv5aekad)jy_u0v2mc+nr'
-TEMPLATE_CONTEXT_PROCESSORS = (
- 'django.contrib.auth.context_processors.auth',
- 'django.core.context_processors.debug',
- 'django.core.context_processors.i18n',
- 'django.core.context_processors.media',
- 'django.core.context_processors.static',
- 'django.core.context_processors.tz',
- 'django.contrib.messages.context_processors.messages',
- 'django.core.context_processors.request',
-)
-
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.Loader',
- 'django.template.loaders.app_directories.Loader',
-)
-
APPEND_SLASH = True
+MIDDLEWARE_CLASSES = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'example_project.wsgi.application'
+
ROOT_URLCONF = 'example_project.urls'
STATIC_URL = '/static/'
@@ -42,6 +54,7 @@ DJANGO_APPS = (
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
+ 'django.contrib.staticfiles',
'django.contrib.admin',
)
diff --git a/example_project/example_project/templates/base.html b/example_project/example_project/templates/base.html
index e625066..c0740d4 100644
--- a/example_project/example_project/templates/base.html
+++ b/example_project/example_project/templates/base.html
@@ -7,7 +7,7 @@
django-embed-video example project
-
+