diff --git a/README.rst b/README.rst index 3bdde27..69231a4 100644 --- a/README.rst +++ b/README.rst @@ -52,7 +52,7 @@ Quick start {% endvideo %} Or embed shortcut: - {{ my_video|embed:'800x600' }} + {% video my_video '800x600' %} #. Use model fields diff --git a/docs/api/embed_video.settings.rst b/docs/api/embed_video.settings.rst index a4d6936..6b26f82 100644 --- a/docs/api/embed_video.settings.rst +++ b/docs/api/embed_video.settings.rst @@ -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`` - diff --git a/docs/conf.py b/docs/conf.py index a400152..edd54b0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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 diff --git a/docs/development/testing.rst b/docs/development/testing.rst index d2e056a..2fcea5b 100644 --- a/docs/development/testing.rst +++ b/docs/development/testing.rst @@ -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 +`_. diff --git a/docs/examples.rst b/docs/examples.rst index 1c7e1d4..52ff185 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -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[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``:: diff --git a/docs/installation.rst b/docs/installation.rst index 78f5050..b4445db 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -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 = ( ... diff --git a/docs/websites.rst b/docs/websites.rst index 5bbbd33..b1a0a46 100644 --- a/docs/websites.rst +++ b/docs/websites.rst @@ -1,8 +1,7 @@ Websites using django-embed-video ============================================== -- `Tchorici.cz `_ (`sources - `_) +- `Tchorici.cz `_ *Are you using django-embed-video? Send pull request!* diff --git a/embed_video/backends.py b/embed_video/backends.py index 0bdaab4..49a4788 100644 --- a/embed_video/backends.py +++ b/embed_video/backends.py @@ -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[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() diff --git a/embed_video/settings.py b/embed_video/settings.py index 42e146b..ea7c0d9 100644 --- a/embed_video/settings.py +++ b/embed_video/settings.py @@ -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) diff --git a/embed_video/templatetags/embed_video_tags.py b/embed_video/templatetags/embed_video_tags.py index b32aaa8..65fca1d 100644 --- a/embed_video/templatetags/embed_video_tags.py +++ b/embed_video/templatetags/embed_video_tags.py @@ -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\d+)x(?P\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) diff --git a/embed_video/tests/custom_backend.py b/embed_video/tests/custom_backend.py index 79a54b3..be14bb8 100644 --- a/embed_video/tests/custom_backend.py +++ b/embed_video/tests/custom_backend.py @@ -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[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}/' diff --git a/embed_video/tests/tests_backend.py b/embed_video/tests/tests_backend.py index ebc05dc..62c8833 100644 --- a/embed_video/tests/tests_backend.py +++ b/embed_video/tests/tests_backend.py @@ -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): diff --git a/embed_video/tests/tests_custom_backend.py b/embed_video/tests/tests_custom_backend.py index a0d9ad7..3246a50 100644 --- a/embed_video/tests/tests_custom_backend.py +++ b/embed_video/tests/tests_custom_backend.py @@ -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/') diff --git a/embed_video/tests/tests_tags.py b/embed_video/tests/tests_tags.py index f341ba0..365c871 100644 --- a/embed_video/tests/tests_tags.py +++ b/embed_video/tests/tests_tags.py @@ -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'' - 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'' - 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'' + 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'' - 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 = ( diff --git a/example_project/example_project/settings.py b/example_project/example_project/settings.py index fcc2db1..7e153ff 100644 --- a/example_project/example_project/settings.py +++ b/example_project/example_project/settings.py @@ -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', diff --git a/example_project/posts/templates/posts/post_detail.html b/example_project/posts/templates/posts/post_detail.html index 241cead..0ab2d9e 100644 --- a/example_project/posts/templates/posts/post_detail.html +++ b/example_project/posts/templates/posts/post_detail.html @@ -9,5 +9,5 @@ Back to list -{{ object.video|embed:"large" }} +{% video object.video "large" %} {% endblock %} diff --git a/example_project/posts/templates/posts/post_list.html b/example_project/posts/templates/posts/post_list.html index 8661435..219089f 100644 --- a/example_project/posts/templates/posts/post_list.html +++ b/example_project/posts/templates/posts/post_list.html @@ -3,6 +3,7 @@ {% load embed_video_tags %} {% block content %} + {{ request.is_secure }}

Posts