Major changes:

- filter ``embed`` is replaced by tag ``video``.
- removed caching for whole backend, caching properties instead.
- HTTP or HTTPS is set by context.is_secure() (just for ``video`` tag).
- backends can be HTTP only (``allow_https = False``).

Minor changes:

- changed theme of docs to RTD theme.
- help about testing HTTPS on development server.
- added and modified tests
This commit is contained in:
Juda Kaleta 2013-12-21 17:12:04 +01:00
parent 6e2750761f
commit 07de8a3af8
17 changed files with 217 additions and 177 deletions

View file

@ -52,7 +52,7 @@ Quick start
{% endvideo %}
Or embed shortcut:
{{ my_video|embed:'800x600' }}
{% video my_video '800x600' %}
#. Use model fields

View file

@ -17,18 +17,3 @@ Default::
)
EMBED_VIDEO_CACHE
------------------
Allows to cache backends data in cache
Default: ``True``
EMBED_VIDEO_CACHE_TIMEOUT
-------------------------
To set how long backends data stay in the cache.
Default: ``3600``

View file

@ -22,6 +22,8 @@ sys.path.insert(0, os.path.abspath('..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'embed_video.tests.django_settings'
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
import embed_video
# -- General configuration -----------------------------------------------------
@ -103,7 +105,13 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'nature'
if not on_rtd: # only import and set the theme if we're building docs locally
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
except ImportError:
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the

View file

@ -31,3 +31,10 @@ Run tests with coverage:
::
nosetests --with-coverage --cover-package=embed_video
Testing HTTPS
-------------
To test HTTPS on development server, `follow this instructions
<http://www.ianlewis.org/en/testing-https-djangos-development-server>`_.

View file

@ -12,39 +12,28 @@ First you have to load the `embed_video_tags` template tags in your template:
{% load embed_video_tags %}
Simple embeding of video:
Embedding of video:
::
{# you can just embed #}
{% video item.video 'small' %}
{# or use variables (with embedding, too) #}
{% video item.video as my_video %}
{{ my_video|embed:'small' }}
URL: {{ my_video.url }}
Thumbnail: {{ my_video.thumbnail }}
Backend: {{ my_video.backend }}
{% video my_video 'small' %}
{% endvideo %}
Default sizes are ``tiny`` (420x315), ``small`` (480x360), ``medium`` (640x480),
``large`` (960x720) and ``huge`` (1280x960). You can set your own size:
::
{{ my_video|embed:'800x600' }}
Usage of variables:
::
{% video item.video as my_video %}
URL: {{ my_video.url }}
Thumbnail: {{ my_video.thumbnail }}
Backend: {{ my_video.backend }}
{% endvideo %}
There is a simplier way, if you don't need work with parameters as
``my_video.url`` or ``my_video.thumbnail`` and you want to use just ``embed``
tag.
::
{{ 'http://www.youtube.com/watch?v=guXyvo2FfLs'|embed:'large' }}
{% video my_video '800x600' %}
.. note::
@ -106,11 +95,12 @@ your custom backend.
re_detect = re.compile(r'http://myvideo\.com/[0-9]+')
re_code = re.compile(r'http://myvideo\.com/(?P<code>[0-9]+)')
pattern_url = 'http://play.myvideo.com/c/%s/'
pattern_thumbnail_url = 'http://thumb.myvideo.com/c/%s/'
allow_https = False
pattern_url = '{protocol}://play.myvideo.com/c/{code}/'
pattern_thumbnail_url = '{protocol}://thumb.myvideo.com/c/{code}/'
You can also overwrite :py:class:`~embed_video.backends.VideoBackend` methods,
if using regular expressions isn't good enough for you.
if using regular expressions isn't well enough.
``my_project/my_project/settings.py``::

View file

@ -6,14 +6,14 @@ Installation
Use pip to install package:
::
.. code-block:: bash
pip install django-embed-video
If you want latest version, you may use Git. It is fresh, but unstable.
::
.. code-block:: bash
pip install git+https://github.com/yetty/django-embed-video.git
@ -24,7 +24,7 @@ Setup
Add ``embed_video`` to :py:data:`~django.settings.INSTALLED_APPS` in your Django
settings.
::
.. code-block:: python
INSTALLED_APPS = (
...

View file

@ -1,8 +1,7 @@
Websites using django-embed-video
==============================================
- `Tchorici.cz <http://www.tchorici.cz>`_ (`sources
<https://github.com/yetty/Tchorici/>`_)
- `Tchorici.cz <http://www.tchorici.cz>`_
*Are you using django-embed-video? Send pull request!*

View file

@ -3,18 +3,15 @@ import requests
import json
try:
import urlparse # py2
except:
import urllib.parse as urlparse # py3
import urlparse
except ImportError:
# support for py3
import urllib.parse as urlparse
from django.utils.functional import cached_property
from .utils import import_by_path
from .settings import EMBED_VIDEO_BACKENDS, EMBED_VIDEO_CACHE
cache = None
if EMBED_VIDEO_CACHE:
from django.core.cache import cache
from .settings import EMBED_VIDEO_BACKENDS
class VideoDoesntExistException(Exception):
@ -76,40 +73,32 @@ class VideoBackend(object):
Example: ``http://static.myvideo.com/thumbs/%s``
"""
allow_https = True
"""
Sets if HTTPS version allowed for specific backend.
"""
is_secure = False
def __init__(self, url):
"""
First it tries to load data from cache and if it don't succeed, run
:py:meth:`init` and then save it to cache.
"""
if not self._restore_cache(url):
self.init(url)
self._set_cache(url)
def _restore_cache(self, url):
"""
Restore data from cache if cache is enabled.
"""
if cache:
data = cache.get(url)
if data:
self.__dict__ = data
return True
def _set_cache(self, url):
"""
Set data to cache if cache is enabled.
"""
if cache:
cache.set(url, self.__dict__)
def init(self, url):
"""
Load all data (:py:meth:`get_code`, :py:meth:`get_url` etc.)
"""
self._url = url
self.backend = self.__class__.__name__
self.code = self.get_code()
self.url = self.get_url()
self._url = url
@cached_property
def code(self):
return self.get_code()
@cached_property
def url(self):
return self.get_url()
@cached_property
def protocol(self):
return 'https' if self.allow_https and self.is_secure else 'http'
@cached_property
def thumbnail(self):
@ -128,9 +117,6 @@ class VideoBackend(object):
return True if klass.re_detect.match(url) else False
def get_code(self):
"""
Returns searched code from URL by :py:data:`re_code`.
"""
match = self.re_code.search(self._url)
if match:
return match.group('code')
@ -139,14 +125,15 @@ class VideoBackend(object):
"""
Returns URL folded from :py:data:`pattern_url` and parsed code.
"""
return self.pattern_url % self.code
return self.pattern_url.format(code=self.code, protocol=self.protocol)
def get_thumbnail_url(self):
"""
Returns thumbnail URL folded from :py:data:`pattern_thumbnail_url` and
parsed code.
"""
return self.pattern_thumbnail_url % self.code
return self.pattern_thumbnail_url.format(code=self.code,
protocol=self.protocol)
def get_embed_code(self, width, height):
"""
@ -182,8 +169,8 @@ class YoutubeBackend(VideoBackend):
re.I | re.X
)
pattern_url = 'http://www.youtube.com/embed/%s?wmode=opaque'
pattern_thumbnail_url = 'http://img.youtube.com/vi/%s/hqdefault.jpg'
pattern_url = '{protocol}://www.youtube.com/embed/{code}?wmode=opaque'
pattern_thumbnail_url = '{protocol}://img.youtube.com/vi/{code}/hqdefault.jpg'
def get_code(self):
code = super(YoutubeBackend, self).get_code()
@ -207,18 +194,14 @@ class VimeoBackend(VideoBackend):
r'^((http(s)?:)?//)?(www\.)?(player\.)?vimeo\.com/.*', re.I
)
re_code = re.compile(r'''vimeo\.com/(video/)?(?P<code>[0-9]+)''', re.I)
pattern_url = '//player.vimeo.com/video/%s'
pattern_info = 'http://vimeo.com/api/v2/video/%s.json'
def init(self, url):
self._url = url
self.code = self.get_code()
super(VimeoBackend, self).init(url)
pattern_url = '{protocol}://player.vimeo.com/video/{code}'
pattern_info = '{protocol}://vimeo.com/api/v2/video/{code}.json'
def get_info(self):
try:
response = requests.get(self.pattern_info % self.code)
response = requests.get(
self.pattern_info.format(code=self.code, protocol=self.protocol)
)
return json.loads(response.text)[0]
except ValueError:
raise VideoDoesntExistException()

View file

@ -6,9 +6,3 @@ EMBED_VIDEO_BACKENDS = getattr(settings, 'EMBED_VIDEO_BACKENDS', (
'embed_video.backends.VimeoBackend',
'embed_video.backends.SoundCloudBackend',
))
EMBED_VIDEO_CACHE = getattr(settings, 'EMBED_VIDEO_CACHE', True)
EMBED_VIDEO_CACHE_TIMEOUT = getattr(settings, 'EMBED_VIDEO_CACHE_TIMEOUT',
3600)

View file

@ -1,5 +1,6 @@
from django.template import Library, Node, TemplateSyntaxError
from django.utils.safestring import mark_safe
import re
from ..backends import detect_backend, VideoBackend
@ -12,7 +13,13 @@ class VideoNode(Node):
Template tag ``video``. It gives access to all
:py:class:`~embed_video.backends.VideoBackend` variables.
Usage:
Usage (shortcut):
.. code-block:: html+django
{% video URL [SIZE] %}
Or as a block:
.. code-block:: html+django
@ -24,6 +31,9 @@ class VideoNode(Node):
.. code-block:: html+django
{% video item.video "large" %}
{% video item.video "340x200" %}
{% video item.video as my_video %}
URL: {{ my_video.url }}
Thumbnail: {{ my_video.thumbnail }}
@ -31,36 +41,105 @@ class VideoNode(Node):
{% endvideo %}
"""
error_msg = ('Syntax error. Expected: ``video source as var``')
error_msg = 'Syntax error. Expected: ``{% video URL ... %}``'
default_size = 'small'
re_size = re.compile('(?P<width>\d+)x(?P<height>\d+)')
def __init__(self, parser, token):
bits = token.split_contents()
self.size = None
self.bits = token.split_contents()
if len(bits) < 4 or bits[-2] != 'as':
try:
self.url = parser.compile_filter(self.bits[1])
except IndexError:
raise TemplateSyntaxError(self.error_msg)
self.url = parser.compile_filter(bits[1])
self.as_var = bits[-1]
self.nodelist_file = parser.parse(('endvideo',))
parser.delete_first_token()
if self.bits[-2] == 'as':
self.nodelist_file = parser.parse(('endvideo',))
parser.delete_first_token()
else:
try:
self.size = parser.compile_filter(self.bits[2])
except IndexError:
self.size = self.default_size
def render(self, context):
url = self.url.resolve(context)
if self.size:
return self.__render_embed(url, context)
else:
return self.__render_block(url, context)
def __render_embed(self, url, context):
size = self.size.resolve(context)
return self.embed(url, size, context=context)
def __render_block(self, url, context):
as_var = self.bits[-1]
context.push()
context[self.as_var] = self.get_backend(url)
context[as_var] = self.get_backend(url, context=context)
output = self.nodelist_file.render(context)
context.pop()
return output
@staticmethod
def get_backend(url, context=None):
backend = detect_backend(url)
def get_backend(backend, context=None):
if not isinstance(backend, VideoBackend):
backend = detect_backend(backend)
if context and 'request' in context:
backend.is_secure = context['request'].is_secure()
return backend
@staticmethod
def embed(url, size, context=None):
"""
Direct render of embed video.
"""
backend = VideoNode.get_backend(url, context=context)
width, height = VideoNode.get_size(size)
return mark_safe(backend.get_embed_code(width=width, height=height))
@staticmethod
def get_size(value):
"""
Predefined sizes:
======== ======== =========
size width height
======== ======== =========
tiny 420 315
small 480 360
medium 640 480
large 960 720
huge 1280 960
======== ======== =========
You can also use custom size - in format ``WIDTHxHEIGHT``
(eg. ``500x400``).
"""
sizes = {
'tiny': (420, 315),
'small': (480, 360),
'medium': (640, 480),
'large': (960, 720),
'huge': (1280, 960),
}
if value in sizes:
return sizes[value]
try:
size = VideoNode.re_size.match(value)
return [int(size.group('width')), int(size.group('height'))]
except AttributeError:
raise TemplateSyntaxError(
'Incorrect size.\nPossible format is WIDTHxHEIGHT or using '
'predefined size ({sizes}).'.format(sizes=', '.join(sizes.iterkeys()))
)
def __iter__(self):
for node in self.nodelist_file:
yield node
@ -72,7 +151,12 @@ class VideoNode(Node):
@register.filter(is_safe=True)
def embed(backend, size='small'):
"""
Shortcut for :py:func:`VideoNode` tag.
.. warning::
.. deprecated:: 0.7
Use :py:func:`VideoNode.embed` instead.
Same like :py:func:`VideoNode.embed` tag but **always uses in secure
HTTP protocol**.
Usage:
@ -85,42 +169,5 @@ def embed(backend, size='small'):
.. code-block:: html+django
{{ 'http://www.youtube.com/watch?v=guXyvo2FfLs'|embed:'large' }}
Predefined sizes:
======== ======== =========
size width height
======== ======== =========
tiny 420 315
small 480 360
medium 640 480
large 960 720
huge 1280 960
======== ======== =========
You can also use custom size - in format ``WIDTHxHEIGHT``
(eg. ``500x400``).
"""
if not isinstance(backend, VideoBackend):
backend = detect_backend(backend)
_size = _embed_get_size(size)
return mark_safe(backend.get_embed_code(width=_size[0], height=_size[1]))
def _embed_get_size(size):
sizes = {
'tiny': (420, 315),
'small': (480, 360),
'medium': (640, 480),
'large': (960, 720),
'huge': (1280, 960),
}
if size in sizes:
return sizes[size]
elif 'x' in size:
return [int(x) for x in size.split('x')]
return VideoNode.embed(backend, size)

View file

@ -7,5 +7,5 @@ class CustomBackend(VideoBackend):
re_detect = re.compile(r'http://myvideo\.com/[0-9]+')
re_code = re.compile(r'http://myvideo\.com/(?P<code>[0-9]+)')
pattern_url = 'http://play.myvideo.com/c/%s/'
pattern_thumbnail_url = 'http://thumb.myvideo.com/c/%s/'
pattern_url = '{protocol}://play.myvideo.com/c/{code}/'
pattern_thumbnail_url = '{protocol}://thumb.myvideo.com/c/{code}/'

View file

@ -14,8 +14,7 @@ class BackendTestMixin(object):
def test_code(self):
for url in self.urls:
backend = self.instance(url[0])
code = backend.get_code()
self.assertEqual(code, url[1])
self.assertEqual(backend.code, url[1])
class VideoBackendTestCase(TestCase):
@ -56,8 +55,13 @@ class YoutubeBackendTestCase(BackendTestMixin, TestCase):
def test_youtube_keyerror(self):
""" Test for issue #7 """
self.assertRaises(UnknownIdException, YoutubeBackend,
'http://youtube.com/watch?id=5')
backend = self.instance('http://youtube.com/watch?id=5')
self.assertRaises(UnknownIdException, backend.get_code)
def test_thumbnail(self):
for url in self.urls:
backend = self.instance(url[0])
self.assertIn(url[1], backend.thumbnail)
class VimeoBackendTestCase(BackendTestMixin, TestCase):

View file

@ -19,6 +19,11 @@ class CustomBackendTestCase(TestCase):
self.assertEqual(self.backend.get_url(),
'http://play.myvideo.com/c/1530/')
def test_url_https(self):
self.backend.is_secure = True
self.assertEqual(self.backend.get_url(),
'https://play.myvideo.com/c/1530/')
def test_thumbnail(self):
self.assertEqual(self.backend.get_thumbnail_url(),
'http://thumb.myvideo.com/c/1530/')

View file

@ -23,11 +23,10 @@ class EmbedVideoNodeTestCase(TestCase):
template = Template("""
{% load embed_video_tags %}
{% video 'http://www.youtube.com/watch?v=jsrRJyHBvzw' as ytb %}
{{ ytb|embed:'large' }}
{% video ytb 'large' %}
{% endvideo %}
""")
rendered = u'<iframe width="960" height="720" src="http://www.youtube.com/embed/jsrRJyHBvzw?wmode=opaque" frameborder="0" allowfullscreen></iframe>'
self.assertEqual(template.render(self._grc()).strip(), rendered)
def test_direct_embed(self):
@ -36,20 +35,33 @@ class EmbedVideoNodeTestCase(TestCase):
{{ 'http://www.youtube.com/watch?v=jsrRJyHBvzw'|embed:'large' }}
""")
rendered = u'<iframe width="960" height="720" src="http://www.youtube.com/embed/jsrRJyHBvzw?wmode=opaque" frameborder="0" allowfullscreen></iframe>'
self.assertEqual(template.render(self._grc()).strip(), rendered)
def test_embed_user_size(self):
def test_direct_embed_tag(self):
template = Template("""
{% load embed_video_tags %}
{% video "http://www.youtube.com/watch?v=jsrRJyHBvzw" "large" %}
""")
rendered = u'<iframe width="960" height="720" src="http://www.youtube.com/embed/jsrRJyHBvzw?wmode=opaque" frameborder="0" allowfullscreen></iframe>'
self.assertEqual(template.render(self._grc()).strip(), rendered)
def test_user_size(self):
template = Template("""
{% load embed_video_tags %}
{% video 'http://www.youtube.com/watch?v=jsrRJyHBvzw' as ytb %}
{{ ytb|embed:'800x800' }}
{% video ytb '800x800' %}
{% endvideo %}
""")
rendered = u'<iframe width="800" height="800" src="http://www.youtube.com/embed/jsrRJyHBvzw?wmode=opaque" frameborder="0" allowfullscreen></iframe>'
self.assertEqual(template.render(self._grc()).strip(), rendered)
def test_wrong_size(self):
template = Template("""
{% load embed_video_tags %}
{% video 'http://www.youtube.com/watch?v=jsrRJyHBvzw' 'so x huge' %}
""")
self.assertRaises(TemplateSyntaxError, template.render, self._grc())
def test_tag_youtube(self):
template = Template("""
{% load embed_video_tags %}
@ -58,7 +70,6 @@ class EmbedVideoNodeTestCase(TestCase):
{% endvideo %}
""")
rendered = 'http://www.youtube.com/embed/jsrRJyHBvzw?wmode=opaque'
self.assertEqual(template.render(self._grc()).strip(), rendered)
def test_tag_vimeo(self):
@ -68,8 +79,7 @@ class EmbedVideoNodeTestCase(TestCase):
{{ vimeo.url }}
{% endvideo %}
""")
rendered = '//player.vimeo.com/video/72304002'
rendered = 'http://player.vimeo.com/video/72304002'
self.assertEqual(template.render(self._grc()).strip(), rendered)
def test_tag_backend_variable_vimeo(self):
@ -104,11 +114,7 @@ class EmbedVideoNodeTestCase(TestCase):
def test_syntax_error(self):
self.token.split_contents.return_value = []
try:
VideoNode(self.parser, self.token)
except TemplateSyntaxError:
assert True
self.assertRaises(TemplateSyntaxError, VideoNode, self.parser, self.token)
def test_repr(self):
self.token.split_contents.return_value = (

View file

@ -14,6 +14,17 @@ 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',

View file

@ -9,5 +9,5 @@
<a href="{% url "posts:list" %}" class="pull-right"><small>Back to list</small></a>
</h1>
{{ object.video|embed:"large" }}
{% video object.video "large" %}
{% endblock %}

View file

@ -3,6 +3,7 @@
{% load embed_video_tags %}
{% block content %}
{{ request.is_secure }}
<h1>Posts</h1>
<table class="table">