mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-04-19 14:31:00 +00:00
Class based embed finders and new embed finder configuration (#2127)
* Allow additional oembed providers to be used (currently all are hardcoded)
* Make per-provider configuration possible
* Choose which finders get used for which provider (eg, force use of oembed for instagram but use embedly for everything else)
* Allow specifying additional parameters for certian providers such as scheme=https to YouTube
This commit is contained in:
parent
cc4f941c0a
commit
01e2d5f4a0
16 changed files with 1204 additions and 277 deletions
|
|
@ -4,6 +4,7 @@ Changelog
|
|||
1.12 (xx.xx.xxxx) - IN DEVELOPMENT
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
* New class-based configuration for media embeds (Karl Hobley)
|
||||
* The admin interface now displays a title of the latest draft (Mikalai Radchuk)
|
||||
* Added multi-select form field to the form builder (dwasyl)
|
||||
* Fix: FieldBlocks in StreamField now call the field's `prepare_value` method (Tim Heap)
|
||||
|
|
|
|||
364
docs/advanced_topics/embeds.rst
Normal file
364
docs/advanced_topics/embeds.rst
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
.. _embedded_content:
|
||||
|
||||
================
|
||||
Embedded content
|
||||
================
|
||||
|
||||
Wagtail supports generating embed code from URLs to content on external
|
||||
providers such as Youtube or Twitter. By default, Wagtail will fetch the embed
|
||||
code directly from the relevant provider's site using the oEmbed protocol.
|
||||
|
||||
Wagtail has a built-in list of the most common providers and this list can be
|
||||
changed :ref:`with a setting <customising_embed_providers>`. Wagtail also supports
|
||||
fetching embed code using `Embedly`_ and :ref:`custom embed finders <custom_embed_finders>`.
|
||||
|
||||
Embedding content on your site
|
||||
==============================
|
||||
|
||||
Wagtail's embeds module should work straight out of the box for most providers.
|
||||
You can use any of the following methods to call the module:
|
||||
|
||||
Rich text
|
||||
---------
|
||||
|
||||
Wagtail's default rich text editor has a "media" icon that allows embeds to be
|
||||
placed into rich text. You don't have to do anything to enable this; just make
|
||||
sure the rich text field's content is being passed through the ``|richtext``
|
||||
filter in the template as this is what calls the embeds module to fetch and
|
||||
nest the embed code.
|
||||
|
||||
``EmbedBlock`` StreamField block type
|
||||
-------------------------------------
|
||||
|
||||
The :class:`~wagtail.wagtailembeds.block.EmbedBlock` block type allows embeds
|
||||
to be placed into a ``StreamField``.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from wagtail.wagtailembeds.blocks import EmbedBlock
|
||||
|
||||
class MyStreamField(blocks.StreamBlock):
|
||||
...
|
||||
|
||||
embed = EmbedBlock()
|
||||
|
||||
``{% embed %}`` tag
|
||||
-------------------
|
||||
|
||||
Syntax: ``{% embed <url> [max_width=<max width>] %}``
|
||||
|
||||
You can nest embeds into a template by passing the URL and an optional
|
||||
``max_width`` argument to the ``{% embed %}`` tag.
|
||||
|
||||
The ``max_width`` argument is sent to the provider when fetching the embed code.
|
||||
|
||||
.. code-block:: html+Django
|
||||
|
||||
{% load wagtailembeds_tags %}
|
||||
|
||||
{# Embed a YouTube video #}
|
||||
{% embed 'https://www.youtube.com/watch?v=SJXMTtvCxRo' %}
|
||||
|
||||
{# This tag can also take the URL from a variable #}
|
||||
{% embed page.video_url %}
|
||||
|
||||
From Python
|
||||
-----------
|
||||
|
||||
You can also call the internal ``get_embed`` function that takes a URL string
|
||||
and returns an ``Embed`` object (see model documentation below). This also
|
||||
takes a ``max_width`` keyword argument that is sent to the provider when
|
||||
fetching the embed code.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from wagtail.wagtailembeds.embeds import get_embed
|
||||
from wagtail.wagtailembeds.exceptions import EmbedException
|
||||
|
||||
try:
|
||||
embed = get_embed('https://www.youtube.com/watch?v=SJXMTtvCxRo')
|
||||
|
||||
print(embed.html)
|
||||
except EmbedException:
|
||||
# Cannot find embed
|
||||
pass
|
||||
|
||||
.. _configuring_embed_finders:
|
||||
|
||||
Configuring embed "finders"
|
||||
===========================
|
||||
|
||||
Embed finders are the modules within Wagtail that are responsible for producing
|
||||
embed code from a URL.
|
||||
|
||||
Embed finders are configured using the ``WAGTAILEMBEDS_FINDERS`` setting. This
|
||||
is a list of finder configurations that are each run in order until one of them
|
||||
successfully returns an embed:
|
||||
|
||||
The default configuration is:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAILEMBEDS_FINDERS = [
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.oembed'
|
||||
}
|
||||
]
|
||||
|
||||
.. _oEmbed:
|
||||
|
||||
oEmbed (default)
|
||||
----------------
|
||||
|
||||
The default embed finder fetches the embed code directly from the content
|
||||
provider using the oEmbed protocol. Wagtail has a built-in list of providers
|
||||
which are all enabled by default. You can find that provider list at the
|
||||
following link:
|
||||
|
||||
https://github.com/wagtail/wagtail/blob/master/wagtail/wagtailembeds/oembed_providers.py
|
||||
|
||||
.. _customising_embed_providers:
|
||||
|
||||
Customising the provider list
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can limit which providers may be used by specifying the list of providers
|
||||
in the finder configuration.
|
||||
|
||||
For example, this configuration will only allow content to be nested from Vimeo
|
||||
and Youtube. It also adds a custom provider:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from wagtail.wagtailembeds.oembed_providers import youtube, vimeo
|
||||
|
||||
# Add a custom provider
|
||||
# Your custom provider must support oEmbed for this to work. You should be
|
||||
# able to find these details in the provider's documentation.
|
||||
# - 'endpoint' is the URL of the oEmbed endpoint that Wagtail will call
|
||||
# - 'urls' specifies which patterns
|
||||
my_custom_provider = {
|
||||
'endpoint': 'https://customvideosite.com/oembed',
|
||||
'urls': [
|
||||
'^http(?:s)?://(?:www\\.)?customvideosite\\.com/[^#?/]+/videos/.+$',
|
||||
]
|
||||
}
|
||||
|
||||
WAGTAILEMBEDS_FINDERS = [
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.oembed',
|
||||
'providers': [youtube, vimeo, my_custom_provider],
|
||||
}
|
||||
]
|
||||
|
||||
Customising an individual provider
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Multiple finders can be chained together. This can be used for customising the
|
||||
configuration for one provider without affecting the others.
|
||||
|
||||
For example, this is how you can instruct Youtube to return videos in HTTPS
|
||||
(which must be done explictly for YouTube):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from wagtail.wagtailembeds.oembed_providers import youtube
|
||||
|
||||
|
||||
WAGTAILEMBEDS_FINDERS = [
|
||||
# Fetches YouTube videos but puts ``?scheme=https`` in the GET parameters
|
||||
# when calling YouTube's oEmbed endpoint
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.oembed',
|
||||
'providers': [youtube],
|
||||
'options': {'scheme': 'https'}
|
||||
},
|
||||
|
||||
# Handles all other oEmbed providers the default way
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.oembed',
|
||||
}
|
||||
]
|
||||
|
||||
.. topic:: How Wagtail uses multiple finders
|
||||
|
||||
If multiple providers can handle a URL (for example, a YouTube video was
|
||||
requested using the configuration above), the topmost finder is chosen to
|
||||
perform the request.
|
||||
|
||||
Wagtail will not try to run any other finder, even if the chosen one didn't
|
||||
return an embed.
|
||||
|
||||
.. _Embedly:
|
||||
|
||||
Embed.ly
|
||||
--------
|
||||
|
||||
`Embed.ly <https://embed.ly>`_ is a paid-for service that can also provide
|
||||
embeds for sites that do not implement the oEmbed protocol.
|
||||
|
||||
They also provide some helpful features such as giving embeds a consistent look
|
||||
and a common video playback API which is useful if your site allows videos to
|
||||
be hosted on different providers and you need to implement custom controls for
|
||||
them.
|
||||
|
||||
Wagtail has built in support for fetching embeds from Embed.ly. To use it, add
|
||||
an embed finder to your ``WAGTAILEMBEDS_FINDERS`` setting that uses the
|
||||
``wagtail.wagtailembeds.finders.oembed`` class and pass it your API key:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAILEMBEDS_FINDERS = [
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.embedly',
|
||||
'key': 'YOUR EMBED.LY KEY HERE'
|
||||
}
|
||||
]
|
||||
|
||||
.. _custom_embed_finders:
|
||||
|
||||
Custom embed finder classes
|
||||
---------------------------
|
||||
|
||||
For complete control, you can create a custom finder class.
|
||||
|
||||
Here's a stub finder class that could be used as a skeleton; please read the
|
||||
docstrings for details of what each method does:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from wagtail.wagtailembeds.finders.base import EmbedFinder
|
||||
|
||||
|
||||
class ExampleFinder(EmbedFinder):
|
||||
def __init__(self, **options):
|
||||
pass
|
||||
|
||||
def accept(self, url):
|
||||
"""
|
||||
Returns True if this finder knows how to fetch an embed for the URL.
|
||||
|
||||
This should not have any side effects (no requests to external servers)
|
||||
"""
|
||||
pass
|
||||
|
||||
def find_embed(self, url, max_width=None):
|
||||
"""
|
||||
Takes a URL and max width and returns a dictionary of information about the
|
||||
content to be used for embedding it on the site.
|
||||
|
||||
This is the part that may make requests to external APIs.
|
||||
"""
|
||||
# TODO: Perform the request
|
||||
|
||||
return {
|
||||
'title': "Title of the content",
|
||||
'author_name': "Author name",
|
||||
'provider_name': "Provider name (eg. YouTube, Vimeo, etc)",
|
||||
'type': "Either 'photo', 'video', 'link' or 'rich'",
|
||||
'thumbnail_url': "URL to thumbnail image",
|
||||
'width': width_in_pixels,
|
||||
'height': height_in_pixels,
|
||||
'html': "<h2>The Embed HTML</h2>,
|
||||
}
|
||||
|
||||
Once you've implemented all of those methods, you just need to add it to your
|
||||
``WAGTAILEMBEDS_FINDERS`` setting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAILEMBEDS_FINDERS = [
|
||||
{
|
||||
'class': 'path.to.your.finder.class.here',
|
||||
# Any other options will be passed as kwargs to the __init__ method
|
||||
}
|
||||
]
|
||||
|
||||
The ``Embed`` model
|
||||
===================
|
||||
|
||||
.. class:: wagtail.wagtailembeds.models.Embed
|
||||
|
||||
Embeds are fetched only once and stored in the database so subsequent requests
|
||||
for an individual embed do not hit the embed finders again.
|
||||
|
||||
.. attribute:: url
|
||||
|
||||
(text)
|
||||
|
||||
The URL of the original content of this embed.
|
||||
|
||||
.. attribute:: max_width
|
||||
|
||||
(integer, nullable)
|
||||
|
||||
The max width that was requested.
|
||||
|
||||
.. attribute:: type
|
||||
|
||||
(text)
|
||||
|
||||
The type of the embed. This can be either 'video', 'photo', 'link' or 'rich'.
|
||||
|
||||
.. attribute:: html
|
||||
|
||||
(text)
|
||||
|
||||
The HTML content of the embed that should be placed on the page
|
||||
|
||||
.. attribute:: title
|
||||
|
||||
(text)
|
||||
|
||||
The title of the content that is being embedded.
|
||||
|
||||
.. attribute:: author_name
|
||||
|
||||
(text)
|
||||
|
||||
The author name of the content that is being embedded.
|
||||
|
||||
.. attribute:: provider_name
|
||||
|
||||
(text)
|
||||
|
||||
The provider name of the content that is being embedded.
|
||||
|
||||
For example: YouTube, Vimeo
|
||||
|
||||
.. attribute:: thumbnail_url
|
||||
|
||||
(text)
|
||||
|
||||
a URL to a thumbnail image of the content that is being embedded.
|
||||
|
||||
.. attribute:: width
|
||||
|
||||
(integer, nullable)
|
||||
|
||||
The width of the embed (images and videos only).
|
||||
|
||||
.. attribute:: height
|
||||
|
||||
(integer, nullable)
|
||||
|
||||
The height of the embed (images and videos only).
|
||||
|
||||
.. attribute:: last_updated
|
||||
|
||||
(datetime)
|
||||
|
||||
The Date/time when this embed was last fetched.
|
||||
|
||||
Deleting embeds
|
||||
---------------
|
||||
|
||||
As long as your embeds configuration is not broken, deleting items in the
|
||||
``Embed`` model should be perfectly safe to do. Wagtail will automatically
|
||||
repopulate the records that are being used on the site.
|
||||
|
||||
You may want to do this if you've changed from oEmbed to Embedly or vice-versa
|
||||
as the embed code they generate may be slightly different and lead to
|
||||
inconsistency on your site.
|
||||
|
|
@ -6,6 +6,7 @@ Advanced topics
|
|||
:maxdepth: 2
|
||||
|
||||
images/index
|
||||
embeds
|
||||
settings
|
||||
deploying
|
||||
performance
|
||||
|
|
|
|||
|
|
@ -206,28 +206,13 @@ Set the number of days (default 7) that search query logs are kept for; these ar
|
|||
Embeds
|
||||
------
|
||||
|
||||
Wagtail uses the oEmbed standard with a large but not comprehensive number of "providers" (Youtube, Vimeo, etc.). You can also use a different embed backend by providing an Embedly key or replacing the embed backend by writing your own embed finder function.
|
||||
Wagtail supports generating embed code from URLs to content on an external
|
||||
providers such as Youtube or Twitter. By default, Wagtail will fetch the embed
|
||||
code directly from the relevant provider's site using the oEmbed protocol.
|
||||
Wagtail has a builtin list of the most common providers.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAILEMBEDS_EMBED_FINDER = 'myapp.embeds.my_embed_finder_function'
|
||||
|
||||
Use a custom embed finder function, which takes a URL and returns a dict with metadata and embeddable HTML. Refer to the ``wagtail.wagtailemebds.embeds`` module source for more information and examples.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# not a working key, get your own!
|
||||
WAGTAILEMBEDS_EMBEDLY_KEY = '253e433d59dc4d2xa266e9e0de0cb830'
|
||||
|
||||
Providing an API key for the Embedly service will use that as a embed backend, with a more extensive list of providers, as well as analytics and other features. For more information, see `Embedly`_.
|
||||
|
||||
.. _Embedly: http://embed.ly/
|
||||
|
||||
To use Embedly, you must also install their Python module:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install embedly
|
||||
The embeds fetching can be fully configured using the ``WAGTAILEMBEDS_FINDERS``
|
||||
setting. This is fully documented in :ref:`configuring_embed_finders`.
|
||||
|
||||
|
||||
Dashboard
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@ Wagtail 1.12 release notes - IN DEVELOPMENT
|
|||
What's new
|
||||
==========
|
||||
|
||||
Improved embed configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
New configuration options for embedded media have been added, to give greater control over how media URLs are converted to embeds, and to make it possible to specify additional media providers beyond the ones built in to Wagtail. For further information, see :ref:`embedded_content`. This feature was developed by Karl Hobley.
|
||||
|
||||
Other features
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
|
@ -26,3 +31,8 @@ Bug fixes
|
|||
|
||||
Upgrade considerations
|
||||
======================
|
||||
|
||||
Old configuration settings for embeds are deprecated
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The configuration settings ``WAGTAILEMBEDS_EMBED_FINDER`` and ``WAGTAILEMBEDS_EMBEDLY_KEY`` have been deprecated in favour of the new ``WAGTAILEMBEDS_FINDERS`` setting. Please see :ref:`configuring_embed_finders` for the new configuration to use.
|
||||
|
|
|
|||
|
|
@ -106,18 +106,10 @@ class TestExpandDbHtml(TestCase):
|
|||
result = expand_db_html(html)
|
||||
self.assertEqual(result, '<a id="1">foo</a>')
|
||||
|
||||
@patch('wagtail.wagtailembeds.finders.oembed.find_embed')
|
||||
def test_expand_db_html_with_embed(self, oembed):
|
||||
oembed.return_value = {
|
||||
'title': 'test title',
|
||||
'author_name': 'test author name',
|
||||
'provider_name': 'test provider name',
|
||||
'type': 'test type',
|
||||
'thumbnail_url': 'test thumbnail url',
|
||||
'width': 'test width',
|
||||
'height': 'test height',
|
||||
'html': 'test html'
|
||||
}
|
||||
@patch('wagtail.wagtailembeds.embeds.get_embed')
|
||||
def test_expand_db_html_with_embed(self, get_embed):
|
||||
from wagtail.wagtailembeds.models import Embed
|
||||
get_embed.return_value = Embed(html='test html')
|
||||
html = '<embed embedtype="media" url="http://www.youtube.com/watch" />'
|
||||
result = expand_db_html(html)
|
||||
self.assertIn('test html', result)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,14 @@ from __future__ import absolute_import, unicode_literals
|
|||
|
||||
from django.apps import AppConfig
|
||||
|
||||
from .finders import get_finders
|
||||
|
||||
|
||||
class WagtailEmbedsAppConfig(AppConfig):
|
||||
name = 'wagtail.wagtailembeds'
|
||||
label = 'wagtailembeds'
|
||||
verbose_name = "Wagtail embeds"
|
||||
|
||||
def ready(self):
|
||||
# Check configuration on startup
|
||||
get_finders()
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ from __future__ import absolute_import, unicode_literals
|
|||
|
||||
from datetime import datetime
|
||||
|
||||
from wagtail.wagtailembeds.finders import get_default_finder
|
||||
from wagtail.wagtailembeds.models import Embed
|
||||
from .exceptions import EmbedUnsupportedProviderException
|
||||
from .finders import get_finders
|
||||
from .models import Embed
|
||||
|
||||
|
||||
def get_embed(url, max_width=None, finder=None):
|
||||
|
|
@ -15,7 +16,13 @@ def get_embed(url, max_width=None, finder=None):
|
|||
|
||||
# Get/Call finder
|
||||
if not finder:
|
||||
finder = get_default_finder()
|
||||
def finder(url, max_width=None):
|
||||
for finder in get_finders():
|
||||
if finder.accept(url):
|
||||
return finder.find_embed(url, max_width=max_width)
|
||||
|
||||
raise EmbedUnsupportedProviderException
|
||||
|
||||
embed_dict = finder(url, max_width)
|
||||
|
||||
# Make sure width and height are valid integers before inserting into database
|
||||
|
|
|
|||
|
|
@ -5,5 +5,9 @@ class EmbedException(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class EmbedUnsupportedProviderException(EmbedException):
|
||||
pass
|
||||
|
||||
|
||||
class EmbedNotFoundException(EmbedException):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,28 +1,34 @@
|
|||
import pprint
|
||||
import sys
|
||||
import warnings
|
||||
from importlib import import_module
|
||||
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils import six
|
||||
from django.conf import settings
|
||||
|
||||
from wagtail.utils.deprecation import RemovedInWagtail114Warning
|
||||
|
||||
|
||||
MOVED_FINDERS = {
|
||||
'wagtail.wagtailembeds.embeds.embedly': 'wagtail.wagtailembeds.finders.embedly',
|
||||
'wagtail.wagtailembeds.embeds.oembed': 'wagtail.wagtailembeds.finders.oembed',
|
||||
'wagtail.wagtailembeds.finders.embedly.embedly': 'wagtail.wagtailembeds.finders.embedly',
|
||||
'wagtail.wagtailembeds.finders.oembed.oembed': 'wagtail.wagtailembeds.finders.oembed',
|
||||
}
|
||||
|
||||
|
||||
def import_finder(dotted_path):
|
||||
def import_finder_class(dotted_path):
|
||||
"""
|
||||
Imports a finder function from a dotted path. If the dotted path points to a
|
||||
module, that module is imported and its "find_embed" function returned.
|
||||
Imports a finder class from a dotted path. If the dotted path points to a
|
||||
module, that module is imported and its "embed_finder_class" class returned.
|
||||
|
||||
If not, this will assume the dotted path points to directly a function and
|
||||
If not, this will assume the dotted path points to directly a class and
|
||||
will attempt to import that instead.
|
||||
"""
|
||||
try:
|
||||
finder_module = import_module(dotted_path)
|
||||
return finder_module.find_embed
|
||||
return finder_module.embed_finder_class
|
||||
except ImportError as e:
|
||||
try:
|
||||
return import_string(dotted_path)
|
||||
|
|
@ -30,20 +36,62 @@ def import_finder(dotted_path):
|
|||
six.reraise(ImportError, e, sys.exc_info()[2])
|
||||
|
||||
|
||||
def get_default_finder():
|
||||
# Check if the user has set the embed finder manually
|
||||
if hasattr(settings, 'WAGTAILEMBEDS_EMBED_FINDER'):
|
||||
def _settings_deprecation_warning(key, suggestion):
|
||||
hint = 'WAGTAILEMBEDS_FINDERS = ' + pprint.pformat(suggestion)
|
||||
warnings.warn(
|
||||
"The `{}` setting is now deprecrated. Please replace this with `{}`".format(key, hint),
|
||||
category=RemovedInWagtail114Warning
|
||||
)
|
||||
|
||||
|
||||
def _get_config_from_settings():
|
||||
if hasattr(settings, 'WAGTAILEMBEDS_FINDERS'):
|
||||
return settings.WAGTAILEMBEDS_FINDERS
|
||||
|
||||
elif hasattr(settings, 'WAGTAILEMBEDS_EMBED_FINDER'):
|
||||
finder_name = settings.WAGTAILEMBEDS_EMBED_FINDER
|
||||
|
||||
if finder_name in MOVED_FINDERS:
|
||||
finder_name = MOVED_FINDERS[finder_name]
|
||||
|
||||
finders = [
|
||||
{
|
||||
'class': finder_name,
|
||||
}
|
||||
]
|
||||
|
||||
_settings_deprecation_warning('WAGTAILEMBEDS_EMBED_FINDER', finders)
|
||||
|
||||
return finders
|
||||
|
||||
elif hasattr(settings, 'WAGTAILEMBEDS_EMBEDLY_KEY'):
|
||||
# Default to Embedly as an embedly key is set
|
||||
finder_name = 'wagtail.wagtailembeds.finders.embedly'
|
||||
finders = [
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.embedly',
|
||||
'key': settings.WAGTAILEMBEDS_EMBEDLY_KEY,
|
||||
}
|
||||
]
|
||||
|
||||
_settings_deprecation_warning('WAGTAILEMBEDS_EMBEDLY_KEY', finders)
|
||||
|
||||
return finders
|
||||
|
||||
else:
|
||||
# Default to oembed
|
||||
finder_name = 'wagtail.wagtailembeds.finders.oembed'
|
||||
# Default to the oembed backend
|
||||
return [
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.oembed',
|
||||
}
|
||||
]
|
||||
|
||||
return import_finder(finder_name)
|
||||
|
||||
def get_finders():
|
||||
finders = []
|
||||
|
||||
for finder_config in _get_config_from_settings():
|
||||
finder_config = finder_config.copy()
|
||||
cls = import_finder_class(finder_config.pop('class'))
|
||||
|
||||
finders.append(cls(**finder_config))
|
||||
|
||||
return finders
|
||||
|
|
|
|||
9
wagtail/wagtailembeds/finders/base.py
Normal file
9
wagtail/wagtailembeds/finders/base.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
|
||||
class EmbedFinder(object):
|
||||
def accept(self, url):
|
||||
return False
|
||||
|
||||
def find_embed(self, url, max_width=None):
|
||||
raise NotImplementedError
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import warnings
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from wagtail.utils.deprecation import RemovedInWagtail114Warning
|
||||
from wagtail.wagtailembeds.exceptions import EmbedException, EmbedNotFoundException
|
||||
|
||||
from .base import EmbedFinder
|
||||
|
||||
|
||||
class EmbedlyException(EmbedException):
|
||||
pass
|
||||
|
|
@ -13,48 +18,77 @@ class AccessDeniedEmbedlyException(EmbedlyException):
|
|||
pass
|
||||
|
||||
|
||||
def embedly(url, max_width=None, key=None):
|
||||
from embedly import Embedly
|
||||
class EmbedlyFinder(EmbedFinder):
|
||||
key = None
|
||||
|
||||
def __init__(self, key=None):
|
||||
if key:
|
||||
self.key = key
|
||||
|
||||
def get_key(self):
|
||||
if self.key:
|
||||
return self.key
|
||||
|
||||
return getattr(settings, 'WAGTAILEMBEDS_EMBEDLY_KEY', None)
|
||||
|
||||
def accept(self, url):
|
||||
# We don't really know what embedly supports so accept everything
|
||||
return True
|
||||
|
||||
def find_embed(self, url, max_width=None, key=None):
|
||||
from embedly import Embedly
|
||||
|
||||
# Get embedly key
|
||||
if key is None:
|
||||
key = self.get_key()
|
||||
|
||||
# Get embedly client
|
||||
client = Embedly(key=key)
|
||||
|
||||
# Call embedly
|
||||
if max_width is not None:
|
||||
oembed = client.oembed(url, maxwidth=max_width, better=False)
|
||||
else:
|
||||
oembed = client.oembed(url, better=False)
|
||||
|
||||
# Check for error
|
||||
if oembed.get('error'):
|
||||
if oembed['error_code'] in [401, 403]:
|
||||
raise AccessDeniedEmbedlyException
|
||||
elif oembed['error_code'] == 404:
|
||||
raise EmbedNotFoundException
|
||||
else:
|
||||
raise EmbedlyException
|
||||
|
||||
# Convert photos into HTML
|
||||
if oembed['type'] == 'photo':
|
||||
html = '<img src="%s" />' % (oembed['url'], )
|
||||
else:
|
||||
html = oembed.get('html')
|
||||
|
||||
# Return embed as a dict
|
||||
return {
|
||||
'title': oembed['title'] if 'title' in oembed else '',
|
||||
'author_name': oembed['author_name'] if 'author_name' in oembed else '',
|
||||
'provider_name': oembed['provider_name'] if 'provider_name' in oembed else '',
|
||||
'type': oembed['type'],
|
||||
'thumbnail_url': oembed.get('thumbnail_url'),
|
||||
'width': oembed.get('width'),
|
||||
'height': oembed.get('height'),
|
||||
'html': html,
|
||||
}
|
||||
|
||||
|
||||
embed_finder_class = EmbedlyFinder
|
||||
|
||||
|
||||
def embedly(url, max_width=None, key=None):
|
||||
warnings.warn(
|
||||
"The `wagtail.wagtailembeds.finders.embedly.embedly` function is now deprecated. Please use the wagtail.wagtailembeds.finders.embedly.Embedly` class instead.",
|
||||
category=RemovedInWagtail114Warning
|
||||
)
|
||||
|
||||
# Get embedly key
|
||||
if key is None:
|
||||
key = settings.WAGTAILEMBEDS_EMBEDLY_KEY
|
||||
|
||||
# Get embedly client
|
||||
client = Embedly(key=key)
|
||||
|
||||
# Call embedly
|
||||
if max_width is not None:
|
||||
oembed = client.oembed(url, maxwidth=max_width, better=False)
|
||||
else:
|
||||
oembed = client.oembed(url, better=False)
|
||||
|
||||
# Check for error
|
||||
if oembed.get('error'):
|
||||
if oembed['error_code'] in [401, 403]:
|
||||
raise AccessDeniedEmbedlyException
|
||||
elif oembed['error_code'] == 404:
|
||||
raise EmbedNotFoundException
|
||||
else:
|
||||
raise EmbedlyException
|
||||
|
||||
# Convert photos into HTML
|
||||
if oembed['type'] == 'photo':
|
||||
html = '<img src="%s" />' % (oembed['url'], )
|
||||
else:
|
||||
html = oembed.get('html')
|
||||
|
||||
# Return embed as a dict
|
||||
return {
|
||||
'title': oembed['title'] if 'title' in oembed else '',
|
||||
'author_name': oembed['author_name'] if 'author_name' in oembed else '',
|
||||
'provider_name': oembed['provider_name'] if 'provider_name' in oembed else '',
|
||||
'type': oembed['type'],
|
||||
'thumbnail_url': oembed.get('thumbnail_url'),
|
||||
'width': oembed.get('width'),
|
||||
'height': oembed.get('height'),
|
||||
'html': html,
|
||||
}
|
||||
|
||||
|
||||
find_embed = embedly
|
||||
return EmbedlyFinder(key=key).find_embed(url, max_width=max_width)
|
||||
|
|
|
|||
|
|
@ -1,53 +1,98 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import json
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from django.utils.six.moves.urllib import request as urllib_request
|
||||
from django.utils.six.moves.urllib.error import URLError
|
||||
from django.utils.six.moves.urllib.parse import urlencode
|
||||
from django.utils.six.moves.urllib.request import Request
|
||||
|
||||
from wagtail.utils.deprecation import RemovedInWagtail114Warning
|
||||
from wagtail.wagtailembeds.exceptions import EmbedNotFoundException
|
||||
from wagtail.wagtailembeds.oembed_providers import get_oembed_provider
|
||||
from wagtail.wagtailembeds.oembed_providers import all_providers
|
||||
|
||||
from .base import EmbedFinder
|
||||
|
||||
|
||||
class OEmbedFinder(EmbedFinder):
|
||||
options = {}
|
||||
_endpoints = None
|
||||
|
||||
def __init__(self, providers=None, options=None):
|
||||
self._endpoints = {}
|
||||
|
||||
for provider in providers or all_providers:
|
||||
patterns = []
|
||||
|
||||
for url in provider['urls']:
|
||||
url = url.replace('{format}', 'json')
|
||||
patterns.append(re.compile(url))
|
||||
|
||||
self._endpoints[provider['endpoint']] = patterns
|
||||
|
||||
if options:
|
||||
self.options = self.options.copy()
|
||||
self.options.update(options)
|
||||
|
||||
def _get_endpoint(self, url):
|
||||
for endpoint, patterns in self._endpoints.items():
|
||||
for pattern in patterns:
|
||||
if re.match(pattern, url):
|
||||
return endpoint
|
||||
|
||||
def accept(self, url):
|
||||
return self._get_endpoint(url) is not None
|
||||
|
||||
def find_embed(self, url, max_width=None):
|
||||
# Find provider
|
||||
endpoint = self._get_endpoint(url)
|
||||
if endpoint is None:
|
||||
raise EmbedNotFoundException
|
||||
|
||||
# Work out params
|
||||
params = self.options.copy()
|
||||
params['url'] = url
|
||||
params['format'] = 'json'
|
||||
if max_width:
|
||||
params['maxwidth'] = max_width
|
||||
|
||||
# Perform request
|
||||
request = Request(endpoint + '?' + urlencode(params))
|
||||
request.add_header('User-agent', 'Mozilla/5.0')
|
||||
try:
|
||||
r = urllib_request.urlopen(request)
|
||||
except URLError:
|
||||
raise EmbedNotFoundException
|
||||
oembed = json.loads(r.read().decode('utf-8'))
|
||||
|
||||
# Convert photos into HTML
|
||||
if oembed['type'] == 'photo':
|
||||
html = '<img src="%s" />' % (oembed['url'], )
|
||||
else:
|
||||
html = oembed.get('html')
|
||||
|
||||
# Return embed as a dict
|
||||
return {
|
||||
'title': oembed['title'] if 'title' in oembed else '',
|
||||
'author_name': oembed['author_name'] if 'author_name' in oembed else '',
|
||||
'provider_name': oembed['provider_name'] if 'provider_name' in oembed else '',
|
||||
'type': oembed['type'],
|
||||
'thumbnail_url': oembed.get('thumbnail_url'),
|
||||
'width': oembed.get('width'),
|
||||
'height': oembed.get('height'),
|
||||
'html': html,
|
||||
}
|
||||
|
||||
|
||||
embed_finder_class = OEmbedFinder
|
||||
|
||||
|
||||
def oembed(url, max_width=None):
|
||||
# Find provider
|
||||
provider = get_oembed_provider(url)
|
||||
if provider is None:
|
||||
raise EmbedNotFoundException
|
||||
warnings.warn(
|
||||
"The `wagtail.wagtailembeds.finders.oembed.oembed` function is now deprecated. Please use the wagtail.wagtailembeds.finders.oembed.OEmbedFinder` class instead.",
|
||||
category=RemovedInWagtail114Warning
|
||||
)
|
||||
|
||||
# Work out params
|
||||
params = {'url': url, 'format': 'json'}
|
||||
if max_width:
|
||||
params['maxwidth'] = max_width
|
||||
|
||||
# Perform request
|
||||
request = Request(provider + '?' + urlencode(params))
|
||||
request.add_header('User-agent', 'Mozilla/5.0')
|
||||
try:
|
||||
r = urllib_request.urlopen(request)
|
||||
except URLError:
|
||||
raise EmbedNotFoundException
|
||||
oembed = json.loads(r.read().decode('utf-8'))
|
||||
|
||||
# Convert photos into HTML
|
||||
if oembed['type'] == 'photo':
|
||||
html = '<img src="%s" />' % (oembed['url'], )
|
||||
else:
|
||||
html = oembed.get('html')
|
||||
|
||||
# Return embed as a dict
|
||||
return {
|
||||
'title': oembed['title'] if 'title' in oembed else '',
|
||||
'author_name': oembed['author_name'] if 'author_name' in oembed else '',
|
||||
'provider_name': oembed['provider_name'] if 'provider_name' in oembed else '',
|
||||
'type': oembed['type'],
|
||||
'thumbnail_url': oembed.get('thumbnail_url'),
|
||||
'width': oembed.get('width'),
|
||||
'height': oembed.get('height'),
|
||||
'html': html,
|
||||
}
|
||||
|
||||
|
||||
find_embed = oembed
|
||||
return OEmbedFinder().find_embed(url, max_width=max_width)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,24 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
OEMBED_ENDPOINTS = {
|
||||
"https://speakerdeck.com/oembed.{format}": [
|
||||
speakerdeck = {
|
||||
"endpoint": "https://speakerdeck.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://speakerdeck\\.com/.+$"
|
||||
],
|
||||
"https://alpha-api.app.net/oembed": [
|
||||
}
|
||||
|
||||
app_net = {
|
||||
"endpoint": "https://alpha-api.app.net/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://alpha\\.app\\.net/[^#?/]+/post/.+$",
|
||||
"^http(?:s)?://photos\\.app\\.net/[^#?/]+/.+$"
|
||||
],
|
||||
"http://www.youtube.com/oembed": [
|
||||
}
|
||||
|
||||
youtube = {
|
||||
"endpoint": "http://www.youtube.com/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/watch.+$",
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/v/.+$",
|
||||
"^http(?:s)?://youtu\\.be/.+$",
|
||||
|
|
@ -21,72 +29,148 @@ OEMBED_ENDPOINTS = {
|
|||
"^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/view_play_list.+$",
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/playlist.+$"
|
||||
],
|
||||
"http://backend.deviantart.com/oembed": [
|
||||
}
|
||||
|
||||
deviantart = {
|
||||
"endpoint": "http://backend.deviantart.com/oembed",
|
||||
"urls": [
|
||||
"^http://(?:[-\\w]+\\.)?deviantart\\.com/art/.+$",
|
||||
"^http://fav\\.me/.+$",
|
||||
"^http://sta\\.sh/.+$",
|
||||
"^http://(?:[-\\w]+\\.)?deviantart\\.com/[^#?/]+#/d.+$"
|
||||
],
|
||||
"http://blip.tv/oembed/": [
|
||||
}
|
||||
|
||||
blip_tv = {
|
||||
"endpoint": "http://blip.tv/oembed/",
|
||||
"urls": [
|
||||
"^http://[-\\w]+\\.blip\\.tv/.+$"
|
||||
],
|
||||
"http://www.dailymotion.com/api/oembed/": [
|
||||
}
|
||||
|
||||
dailymotion = {
|
||||
"endpoint": "http://www.dailymotion.com/api/oembed/",
|
||||
"urls": [
|
||||
"^http://[-\\w]+\\.dailymotion\\.com/.+$"
|
||||
],
|
||||
"http://www.flickr.com/services/oembed/": [
|
||||
}
|
||||
|
||||
flikr = {
|
||||
"endpoint": "http://www.flickr.com/services/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://[-\\w]+\\.flickr\\.com/photos/.+$",
|
||||
"^http(?:s)?://flic\\.kr\\.com/.+$"
|
||||
],
|
||||
"http://www.hulu.com/api/oembed.{format}": [
|
||||
}
|
||||
|
||||
hulu = {
|
||||
"endpoint": "http://www.hulu.com/api/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://www\\.hulu\\.com/watch/.+$"
|
||||
],
|
||||
"http://www.nfb.ca/remote/services/oembed/": [
|
||||
}
|
||||
|
||||
nfb = {
|
||||
"endpoint": "http://www.nfb.ca/remote/services/oembed/",
|
||||
"urls": [
|
||||
"^http://(?:[-\\w]+\\.)?nfb\\.ca/film/.+$"
|
||||
],
|
||||
"http://qik.com/api/oembed.{format}": [
|
||||
}
|
||||
|
||||
qik = {
|
||||
"endpoint": "http://qik.com/api/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://qik\\.com/.+$",
|
||||
"^http://qik\\.ly/.+$"
|
||||
],
|
||||
"http://revision3.com/api/oembed/": [
|
||||
}
|
||||
|
||||
revision3 = {
|
||||
"endpoint": "http://revision3.com/api/oembed/",
|
||||
"urls": [
|
||||
"^http://[-\\w]+\\.revision3\\.com/.+$"
|
||||
],
|
||||
"http://www.scribd.com/services/oembed": [
|
||||
}
|
||||
|
||||
scribd = {
|
||||
"endpoint": "http://www.scribd.com/services/oembed",
|
||||
"urls": [
|
||||
"^http://[-\\w]+\\.scribd\\.com/.+$"
|
||||
],
|
||||
"http://www.viddler.com/oembed/": [
|
||||
}
|
||||
|
||||
viddler = {
|
||||
"endpoint": "http://www.viddler.com/oembed/",
|
||||
"urls": [
|
||||
"^http://[-\\w]+\\.viddler\\.com/v/.+$",
|
||||
"^http://[-\\w]+\\.viddler\\.com/explore/.+$"
|
||||
],
|
||||
"http://www.vimeo.com/api/oembed.{format}": [
|
||||
}
|
||||
|
||||
vimeo = {
|
||||
"endpoint": "http://www.vimeo.com/api/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?vimeo\\.com/.+$",
|
||||
"^http(?:s)?://player\\.vimeo\\.com/.+$"
|
||||
],
|
||||
"http://dotsub.com/services/oembed": [
|
||||
}
|
||||
|
||||
dotsub = {
|
||||
"endpoint": "http://dotsub.com/services/oembed",
|
||||
"urls": [
|
||||
"^http://dotsub\\.com/view/.+$"
|
||||
],
|
||||
"http://www.yfrog.com/api/oembed": [
|
||||
}
|
||||
|
||||
yfrog = {
|
||||
"endpoint": "http://www.yfrog.com/api/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?yfrog\\.com/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?yfrog\\.us/.+$"
|
||||
],
|
||||
"http://clikthrough.com/services/oembed": [
|
||||
}
|
||||
|
||||
clickthrough = {
|
||||
"endpoint": "http://clikthrough.com/services/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?clikthrough\\.com/.+$"
|
||||
],
|
||||
"http://www.kinomap.com/oembed": [
|
||||
}
|
||||
|
||||
kinomap = {
|
||||
"endpoint": "http://www.kinomap.com/oembed",
|
||||
"urls": [
|
||||
"^http://[-\\w]+\\.kinomap\\.com/.+$"
|
||||
],
|
||||
"https://photobucket.com/oembed": [
|
||||
}
|
||||
|
||||
photobucket = {
|
||||
"endpoint": "https://photobucket.com/oembed",
|
||||
"urls": [
|
||||
"^http://(?:[-\\w]+\\.)?photobucket\\.com/albums/.+$",
|
||||
"^http://(?:[-\\w]+\\.)?photobucket\\.com/groups/.+$"
|
||||
],
|
||||
"http://api.instagram.com/oembed": [
|
||||
}
|
||||
|
||||
instagram = {
|
||||
"endpoint": "http://api.instagram.com/oembed",
|
||||
"urls": [
|
||||
"^http://instagr\\.am/p/.+$",
|
||||
"^http[s]?://(?:www\\.)?instagram\\.com/p/.+$"
|
||||
],
|
||||
"https://www.facebook.com/plugins/video/oembed.{format}": [
|
||||
}
|
||||
|
||||
facebook_video = {
|
||||
"endpoint": "https://www.facebook.com/plugins/video/oembed.{format}",
|
||||
"urls": [
|
||||
"^https://(?:www\\.)?facebook\\.com/.+?/videos/.+$",
|
||||
"^https://(?:www\\.)?facebook\\.com/video\\.php\\?(?:v|id)=.+$",
|
||||
],
|
||||
"https://www.facebook.com/plugins/post/oembed.{format}": [
|
||||
}
|
||||
|
||||
facebook_post = {
|
||||
"endpoint": "https://www.facebook.com/plugins/post/oembed.{format}",
|
||||
"urls": [
|
||||
"^https://(?:www\\.)?facebook\\.com/.+?/(?:posts|activity)/.+$",
|
||||
"^https://(?:www\\.)?facebook\\.com/photo\\.php\\?fbid=.+$",
|
||||
"^https://(?:www\\.)?facebook\\.com/(?:photos|questions)/.+$",
|
||||
|
|
@ -98,252 +182,479 @@ OEMBED_ENDPOINTS = {
|
|||
# Works for posts with a single photo
|
||||
"^https://(?:www\\.)?facebook\\.com/.+?/photos/.+$",
|
||||
],
|
||||
"https://www.slideshare.net/api/oembed/2": [
|
||||
}
|
||||
|
||||
slideshare = {
|
||||
"endpoint": "https://www.slideshare.net/api/oembed/2",
|
||||
"urls": [
|
||||
"^http://www\\.slideshare\\.net/.+$"
|
||||
],
|
||||
"http://tv.majorleaguegaming.com/oembed": [
|
||||
}
|
||||
|
||||
major_league_gaming = {
|
||||
"endpoint": "http://tv.majorleaguegaming.com/oembed",
|
||||
"urls": [
|
||||
"^http://mlg\\.tv/.+$",
|
||||
"^http://tv\\.majorleaguegaming\\.com/.+$"
|
||||
],
|
||||
"http://my.opera.com/service/oembed": [
|
||||
}
|
||||
|
||||
opera = {
|
||||
"endpoint": "http://my.opera.com/service/oembed",
|
||||
"urls": [
|
||||
"^http://my\\.opera\\.com/.+$"
|
||||
],
|
||||
"http://skitch.com/oembed": [
|
||||
}
|
||||
|
||||
skitch = {
|
||||
"endpoint": "http://skitch.com/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?skitch\\.com/.+$",
|
||||
"^http://skit\\.ch/.+$"
|
||||
],
|
||||
"https://api.twitter.com/1/statuses/oembed.{format}": [
|
||||
}
|
||||
|
||||
twitter = {
|
||||
"endpoint": "https://api.twitter.com/1/statuses/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://twitter\\.com/(?:#!)?[^#?/]+/status/.+$"
|
||||
],
|
||||
"https://soundcloud.com/oembed": [
|
||||
}
|
||||
|
||||
soundcloud = {
|
||||
"endpoint": "https://soundcloud.com/oembed",
|
||||
"urls": [
|
||||
"^https://soundcloud\\.com/[^#?/]+/.+$"
|
||||
],
|
||||
"http://www.collegehumor.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
collegehumor = {
|
||||
"endpoint": "http://www.collegehumor.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://(?:www\\.)?collegehumor\\.com/video/.+$",
|
||||
"^http://(?:www\\.)?collegehumor\\.com/video:.+$"
|
||||
],
|
||||
"http://www.polleverywhere.com/services/oembed/": [
|
||||
}
|
||||
|
||||
polleverywhere = {
|
||||
"endpoint": "http://www.polleverywhere.com/services/oembed/",
|
||||
"urls": [
|
||||
"^http://www\\.polleverywhere\\.com/polls/.+$",
|
||||
"^http://www\\.polleverywhere\\.com/multiple_choice_polls/.+$",
|
||||
"^http://www\\.polleverywhere\\.com/free_text_polls/.+$"
|
||||
],
|
||||
"http://www.ifixit.com/Embed": [
|
||||
}
|
||||
|
||||
ifixit = {
|
||||
"endpoint": "http://www.ifixit.com/Embed",
|
||||
"urls": [
|
||||
"^http://www\\.ifixit\\.com/[^#?/]+/[^#?/]+/.+$"
|
||||
],
|
||||
"http://api.smugmug.com/services/oembed/": [
|
||||
}
|
||||
|
||||
smugmug = {
|
||||
"endpoint": "http://api.smugmug.com/services/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?smugmug\\.com/[^#?/]+/.+$"
|
||||
],
|
||||
"https://github.com/api/oembed": [
|
||||
}
|
||||
|
||||
github_gist = {
|
||||
"endpoint": "https://github.com/api/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://gist\\.github\\.com/.+$"
|
||||
],
|
||||
"http://animoto.com/services/oembed": [
|
||||
}
|
||||
|
||||
animoto = {
|
||||
"endpoint": "http://animoto.com/services/oembed",
|
||||
"urls": [
|
||||
"^http://animoto\\.com/play/.+$"
|
||||
],
|
||||
"http://www.rdio.com/api/oembed": [
|
||||
}
|
||||
|
||||
rdio = {
|
||||
"endpoint": "http://www.rdio.com/api/oembed",
|
||||
"urls": [
|
||||
"^http://(?:wwww\\.)?rdio\\.com/people/[^#?/]+/playlists/.+$",
|
||||
"^http://[-\\w]+\\.rdio\\.com/artist/[^#?/]+/album/.+$"
|
||||
],
|
||||
"http://api.5min.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
five_min = {
|
||||
"endpoint": "http://api.5min.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://www\\.5min\\.com/video/.+$"
|
||||
],
|
||||
"http://500px.com/photo/{1}/oembed.{format}": [
|
||||
}
|
||||
|
||||
five_hundred_px = {
|
||||
"endpoint": "http://500px.com/photo/{1}/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://500px\\.com/photo/([^#?/]+)(?:.+)?$"
|
||||
],
|
||||
"http://api.dipdive.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
dipdive = {
|
||||
"endpoint": "http://api.dipdive.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://[-\\w]+\\.dipdive\\.com/media/.+$"
|
||||
],
|
||||
"http://video.yandex.ru/oembed.{format}": [
|
||||
}
|
||||
|
||||
yandex = {
|
||||
"endpoint": "http://video.yandex.ru/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://video\\.yandex\\.ru/users/[^#?/]+/view/.+$"
|
||||
],
|
||||
"https://www.mixcloud.com/oembed/": [
|
||||
}
|
||||
|
||||
mixcloud = {
|
||||
"endpoint": "https://www.mixcloud.com/oembed/",
|
||||
"urls": [
|
||||
"^https?://(?:www\\.)?mixcloud\\.com/.+$"
|
||||
],
|
||||
"http://www.kickstarter.com/services/oembed": [
|
||||
}
|
||||
|
||||
kickstarter = {
|
||||
"endpoint": "http://www.kickstarter.com/services/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)://[-\\w]+\\.kickstarter\\.com/projects/.+$"
|
||||
],
|
||||
"http://coub.com/api/oembed.{format}": [
|
||||
}
|
||||
|
||||
coub = {
|
||||
"endpoint": "http://coub.com/api/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://coub\\.com/view/.+$",
|
||||
"^http(?:s)?://coub\\.com/embed/.+$"
|
||||
],
|
||||
"http://www.screenr.com/api/oembed.{format}": [
|
||||
}
|
||||
|
||||
screenr = {
|
||||
"endpoint": "http://www.screenr.com/api/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://www\\.screenr\\.com/.+$"
|
||||
],
|
||||
"http://www.funnyordie.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
funny_or_die = {
|
||||
"endpoint": "http://www.funnyordie.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://www\\.funnyordie\\.com/videos/.+$"
|
||||
],
|
||||
"http://fast.wistia.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
wistia = {
|
||||
"endpoint": "http://fast.wistia.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^https?://([^/]+\.)?(wistia.com|wi.st)/(medias|embed)/.+$"
|
||||
],
|
||||
"http://www.ustream.tv/oembed": [
|
||||
}
|
||||
|
||||
ustream = {
|
||||
"endpoint": "http://www.ustream.tv/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?ustream\\.tv/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?ustream\\.com/.+$",
|
||||
"^http://ustre\\.am/.+$"
|
||||
],
|
||||
"http://wordpress.tv/oembed/": [
|
||||
}
|
||||
|
||||
wordpress = {
|
||||
"endpoint": "http://wordpress.tv/oembed/",
|
||||
"urls": [
|
||||
"^http://wordpress\\.tv/.+$"
|
||||
],
|
||||
"http://polldaddy.com/oembed/": [
|
||||
}
|
||||
|
||||
polldaddy = {
|
||||
"endpoint": "http://polldaddy.com/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?polldaddy\\.com/.+$"
|
||||
],
|
||||
"http://api.bambuser.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
bambuser = {
|
||||
"endpoint": "http://api.bambuser.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://bambuser\\.com/channel/[^#?/]+/broadcast/.+$",
|
||||
"^http://bambuser\\.com/channel/.+$",
|
||||
"^http://bambuser\\.com/v/.+$"
|
||||
],
|
||||
"http://www.ted.com/talks/oembed.{format}": [
|
||||
}
|
||||
|
||||
ted = {
|
||||
"endpoint": "http://www.ted.com/talks/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?ted\\.com/talks/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?ted\\.com/talks/lang/[^#?/]+/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?ted\\.com/index\\.php/talks/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?ted\\.com/index\\.php/talks/lang/[^#?/]+/.+$"
|
||||
],
|
||||
"http://chirb.it/oembed.{format}": [
|
||||
}
|
||||
|
||||
chirb = {
|
||||
"endpoint": "http://chirb.it/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://chirb\\.it/.+$"
|
||||
],
|
||||
"https://www.circuitlab.com/circuit/oembed/": [
|
||||
}
|
||||
|
||||
circuitlab = {
|
||||
"endpoint": "https://www.circuitlab.com/circuit/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?circuitlab\\.com/circuit/.+$"
|
||||
],
|
||||
"http://api.geograph.org.uk/api/oembed": [
|
||||
}
|
||||
|
||||
geograph_uk = {
|
||||
"endpoint": "http://api.geograph.org.uk/api/oembed",
|
||||
"urls": [
|
||||
"^http://(?:[-\\w]+\\.)?geograph\\.org\\.uk/.+$",
|
||||
"^http://(?:[-\\w]+\\.)?geograph\\.co\\.uk/.+$",
|
||||
"^http://(?:[-\\w]+\\.)?geograph\\.ie/.+$"
|
||||
],
|
||||
"http://geo.hlipp.de/restapi.php/api/oembed": [
|
||||
}
|
||||
|
||||
hlipp = {
|
||||
"endpoint": "http://geo.hlipp.de/restapi.php/api/oembed",
|
||||
"urls": [
|
||||
"^http://geo-en\\.hlipp\\.de/.+$",
|
||||
"^http://geo\\.hlipp\\.de/.+$",
|
||||
"^http://germany\\.geograph\\.org/.+$"
|
||||
],
|
||||
"http://www.geograph.org.gg/api/oembed": [
|
||||
}
|
||||
|
||||
geograph_gg = {
|
||||
"endpoint": "http://www.geograph.org.gg/api/oembed",
|
||||
"urls": [
|
||||
"^http://(?:[-\\w]+\\.)?geograph\\.org\\.gg/.+$",
|
||||
"^http://(?:[-\\w]+\\.)?geograph\\.org\\.je/.+$",
|
||||
"^http://channel-islands\\.geograph\\.org/.+$",
|
||||
"^http://channel-islands\\.geographs\\.org/.+$",
|
||||
"^http://(?:[-\\w]+\\.)?channel\\.geographs\\.org/.+$"
|
||||
],
|
||||
"http://vzaar.com/api/videos/{1}.{format}": [
|
||||
}
|
||||
|
||||
vzaar = {
|
||||
"endpoint": "http://vzaar.com/api/videos/{1}.{format}",
|
||||
"urls": [
|
||||
"^http://(?:www\\.)?vzaar\\.com/videos/([^#?/]+)(?:.+)?$",
|
||||
"^http://www\\.vzaar\\.tv/([^#?/]+)(?:.+)?$",
|
||||
"^http://vzaar\\.tv/([^#?/]+)(?:.+)?$",
|
||||
"^http://vzaar\\.me/([^#?/]+)(?:.+)?$",
|
||||
"^http://[-\\w]+\\.vzaar\\.me/([^#?/]+)(?:.+)?$"
|
||||
],
|
||||
"http://api.minoto-video.com/services/oembed.{format}": [
|
||||
}
|
||||
|
||||
minoto = {
|
||||
"endpoint": "http://api.minoto-video.com/services/oembed.{format}",
|
||||
"urls": [
|
||||
"^http://api\\.minoto-video\\.com/publishers/[^#?/]+/videos/.+$",
|
||||
"^http://dashboard\\.minoto-video\\.com/main/video/details/.+$",
|
||||
"^http://embed\\.minoto-video\\.com/.+$"
|
||||
],
|
||||
"http://www.videojug.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
videojug = {
|
||||
"endpoint": "http://www.videojug.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?videojug\\.com/film/.+$",
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?videojug\\.com/payer/.+$",
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?videojug\\.com/interview/.+$"
|
||||
],
|
||||
"http://videos.sapo.pt/oembed": [
|
||||
}
|
||||
|
||||
sapo = {
|
||||
"endpoint": "http://videos.sapo.pt/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://videos\\.sapo\\.pt/.+$"
|
||||
],
|
||||
"http://vhx.tv/services/oembed.{format}": [
|
||||
}
|
||||
|
||||
vhx_tv = {
|
||||
"endpoint": "http://vhx.tv/services/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?vhx\\.tv/.+$"
|
||||
],
|
||||
"http://api.justin.tv/api/embed/from_url.{format}": [
|
||||
}
|
||||
|
||||
justin_tv = {
|
||||
"endpoint": "http://api.justin.tv/api/embed/from_url.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?justin\\.tv/.+$"
|
||||
],
|
||||
"http://official.fm/services/oembed.{format}": [
|
||||
}
|
||||
|
||||
official_fm = {
|
||||
"endpoint": "http://official.fm/services/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://official\\.fm/.+$"
|
||||
],
|
||||
"http://huffduffer.com/oembed": [
|
||||
}
|
||||
|
||||
huffduffer = {
|
||||
"endpoint": "http://huffduffer.com/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?huffduffer\\.com/[^#?/]+/.+$"
|
||||
],
|
||||
"https://embed.spotify.com/oembed/": [
|
||||
}
|
||||
|
||||
spotify = {
|
||||
"endpoint": "https://embed.spotify.com/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://open\\.spotify\\.com/.+$",
|
||||
"^http(?:s)?://spoti\\.fi/.+$"
|
||||
],
|
||||
"http://shoudio.com/api/oembed": [
|
||||
}
|
||||
|
||||
shoudio = {
|
||||
"endpoint": "http://shoudio.com/api/oembed",
|
||||
"urls": [
|
||||
"^http://shoudio\\.com/.+$",
|
||||
"^http://shoud\\.io/.+$"
|
||||
],
|
||||
"http://api.mobypicture.com/oEmbed": [
|
||||
}
|
||||
|
||||
mobypicture = {
|
||||
"endpoint": "http://api.mobypicture.com/oEmbed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?mobypicture\\.com/user/[^#?/]+/view/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?moby\\.to/.+$"
|
||||
],
|
||||
"http://www.23hq.com/23/oembed": [
|
||||
}
|
||||
|
||||
twenty_three_hq = {
|
||||
"endpoint": "http://www.23hq.com/23/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?23hq\\.com/[^#?/]+/photo/.+$"
|
||||
],
|
||||
"http://gmep.org/oembed.{format}": [
|
||||
}
|
||||
|
||||
gmep = {
|
||||
"endpoint": "http://gmep.org/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?gmep\\.org/.+$",
|
||||
"^http(?:s)?://gmep\\.imeducate\\.com/.+$"
|
||||
],
|
||||
"http://oembed.urtak.com/1/oembed": [
|
||||
}
|
||||
|
||||
urtak = {
|
||||
"endpoint": "http://oembed.urtak.com/1/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:[-\\w]+\\.)?urtak\\.com/.+$"
|
||||
],
|
||||
"http://cacoo.com/oembed.{format}": [
|
||||
}
|
||||
|
||||
cacoo = {
|
||||
"endpoint": "http://cacoo.com/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://cacoo\\.com/.+$"
|
||||
],
|
||||
"http://api.dailymile.com/oembed": [
|
||||
}
|
||||
|
||||
dailymile = {
|
||||
"endpoint": "http://api.dailymile.com/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?dailymile\\.com/people/[^#?/]+/entries/.+$"
|
||||
],
|
||||
"http://www.dipity.com/oembed/timeline/": [
|
||||
}
|
||||
|
||||
dipity = {
|
||||
"endpoint": "http://www.dipity.com/oembed/timeline/",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?dipity\\.com/timeline/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?dipity\\.com/voaweb/.+$"
|
||||
],
|
||||
"https://sketchfab.com/oembed": [
|
||||
}
|
||||
|
||||
sketchfab = {
|
||||
"endpoint": "https://sketchfab.com/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://sketchfab\\.com/show/.+$"
|
||||
],
|
||||
"https://api.meetup.com/oembed": [
|
||||
}
|
||||
|
||||
meetup = {
|
||||
"endpoint": "https://api.meetup.com/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?meetup\\.com/.+$",
|
||||
"^http(?:s)?://(?:www\\.)?meetup\\.ps/.+$"
|
||||
],
|
||||
"https://roomshare.jp/oembed.{format}": [
|
||||
}
|
||||
|
||||
roomshare = {
|
||||
"endpoint": "https://roomshare.jp/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?roomshare\\.jp/(?:en/)?post/.+$"
|
||||
],
|
||||
"http://crowdranking.com/api/oembed.{format}": [
|
||||
}
|
||||
|
||||
crowd_ranking = {
|
||||
"endpoint": "http://crowdranking.com/api/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://crowdranking\\.com/crowdrankings/.+$",
|
||||
"^http(?:s)?://crowdranking\\.com/rankings/.+$",
|
||||
"^http(?:s)?://crowdranking\\.com/topics/.+$",
|
||||
"^http(?:s)?://crowdranking\\.com/widgets/.+$",
|
||||
"^http(?:s)?://crowdranking\\.com/r/.+$"
|
||||
],
|
||||
"http://openapi.etsy.com/svc/oembed/": [
|
||||
}
|
||||
|
||||
etsy = {
|
||||
"endpoint": "http://openapi.etsy.com/svc/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?etsy\\.com/listing/.+$"
|
||||
],
|
||||
"https://audioboom.com/publishing/oembed.{format}": [
|
||||
}
|
||||
|
||||
audioboom = {
|
||||
"endpoint": "https://audioboom.com/publishing/oembed.{format}",
|
||||
"urls": [
|
||||
"^http(?:s)?://audioboom\\.com/boos/.+$"
|
||||
],
|
||||
"http://demo.clikthrough.com/services/oembed/": [
|
||||
}
|
||||
|
||||
clikthrough = {
|
||||
"endpoint": "http://demo.clikthrough.com/services/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://demo\\.clikthrough\\.com/theater/video/.+$"
|
||||
],
|
||||
"http://www.ifttt.com/oembed/": [
|
||||
}
|
||||
|
||||
ifttt = {
|
||||
"endpoint": "http://www.ifttt.com/oembed/",
|
||||
"urls": [
|
||||
"^http(?:s)?://ifttt\\.com/recipes/.+$"
|
||||
],
|
||||
}
|
||||
|
||||
# Added 11th December 2014 - http://developers.issuu.com/api/oembed.html
|
||||
"http://issuu.com/oembed": [
|
||||
issuu = {
|
||||
"endpoint": "http://issuu.com/oembed",
|
||||
"urls": [
|
||||
"^http(?:s)?://(?:www\\.)?issuu\\.com/[^#?/]+/docs/.+$"
|
||||
],
|
||||
"https://www.tumblr.com/oembed/1.0": [
|
||||
}
|
||||
|
||||
tumblr = {
|
||||
"endpoint": "https://www.tumblr.com/oembed/1.0",
|
||||
"urls": [
|
||||
"^http(?:s)?://.+?\\.tumblr\\.com/post/.+$",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# Compile endpoints into regular expression objects
|
||||
|
||||
def compile_endpoints():
|
||||
endpoints = {}
|
||||
for endpoint in OEMBED_ENDPOINTS.keys():
|
||||
endpoint_key = endpoint.replace('{format}', 'json')
|
||||
|
||||
endpoints[endpoint_key] = []
|
||||
for pattern in OEMBED_ENDPOINTS[endpoint]:
|
||||
endpoints[endpoint_key].append(re.compile(pattern))
|
||||
|
||||
return endpoints
|
||||
|
||||
|
||||
OEMBED_ENDPOINTS_COMPILED = compile_endpoints()
|
||||
|
||||
|
||||
def get_oembed_provider(url):
|
||||
for endpoint in OEMBED_ENDPOINTS_COMPILED.keys():
|
||||
for pattern in OEMBED_ENDPOINTS_COMPILED[endpoint]:
|
||||
if re.match(pattern, url):
|
||||
return endpoint
|
||||
|
||||
return
|
||||
all_providers = [
|
||||
speakerdeck, app_net, youtube, deviantart, blip_tv, dailymotion, flikr,
|
||||
hulu, nfb, qik, revision3, scribd, viddler, vimeo, dotsub, yfrog,
|
||||
clickthrough, kinomap, photobucket, instagram, facebook_video,
|
||||
facebook_post, slideshare,
|
||||
major_league_gaming, opera, skitch, twitter, soundcloud, collegehumor,
|
||||
polleverywhere, ifixit, smugmug, github_gist, animoto, rdio, five_min,
|
||||
five_hundred_px, dipdive, yandex, mixcloud, kickstarter, coub, screenr,
|
||||
funny_or_die, wistia, ustream, wordpress, polldaddy, bambuser, ted, chirb,
|
||||
circuitlab, geograph_uk, hlipp, geograph_gg, vzaar, minoto, videojug, sapo,
|
||||
vhx_tv, justin_tv, official_fm, huffduffer, spotify, shoudio, mobypicture,
|
||||
twenty_three_hq, gmep, urtak, cacoo, dailymile, dipity, sketchfab, meetup,
|
||||
roomshare, crowd_ranking, etsy, audioboom, clikthrough, ifttt, issuu, tumblr
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,13 +13,15 @@ from mock import patch
|
|||
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
from wagtail.wagtailcore import blocks
|
||||
from wagtail.wagtailembeds import oembed_providers
|
||||
from wagtail.wagtailembeds.blocks import EmbedBlock, EmbedValue
|
||||
from wagtail.wagtailembeds.embeds import get_embed
|
||||
from wagtail.wagtailembeds.exceptions import EmbedNotFoundException
|
||||
from wagtail.wagtailembeds.finders import get_default_finder
|
||||
from wagtail.wagtailembeds.finders.embedly import embedly as wagtail_embedly
|
||||
from wagtail.wagtailembeds.exceptions import (
|
||||
EmbedNotFoundException, EmbedUnsupportedProviderException)
|
||||
from wagtail.wagtailembeds.finders import get_finders
|
||||
from wagtail.wagtailembeds.finders.embedly import EmbedlyFinder as EmbedlyFinder
|
||||
from wagtail.wagtailembeds.finders.embedly import AccessDeniedEmbedlyException, EmbedlyException
|
||||
from wagtail.wagtailembeds.finders.oembed import oembed as wagtail_oembed
|
||||
from wagtail.wagtailembeds.finders.oembed import OEmbedFinder as OEmbedFinder
|
||||
from wagtail.wagtailembeds.models import Embed
|
||||
from wagtail.wagtailembeds.rich_text import MediaEmbedHandler
|
||||
from wagtail.wagtailembeds.templatetags.wagtailembeds_tags import embed_tag
|
||||
|
|
@ -31,42 +33,136 @@ except ImportError:
|
|||
no_embedly = True
|
||||
|
||||
|
||||
class TestGetDefaultFinder(TestCase):
|
||||
class TestGetFinders(TestCase):
|
||||
def test_defaults_to_oembed(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_oembed)
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], OEmbedFinder)
|
||||
|
||||
# New WAGTAILEMBEDS_FINDERS setting
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_FINDERS=[
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.oembed'
|
||||
}
|
||||
])
|
||||
def test_new_find_oembed(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], OEmbedFinder)
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_FINDERS=[
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.embedly',
|
||||
'key': 'foo',
|
||||
}
|
||||
])
|
||||
def test_new_find_embedly(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], EmbedlyFinder)
|
||||
self.assertEqual(finders[0].get_key(), 'foo')
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_FINDERS=[
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.oembed',
|
||||
'options': {'foo': 'bar'}
|
||||
}
|
||||
])
|
||||
def test_new_find_oembed_with_options(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], OEmbedFinder)
|
||||
self.assertEqual(finders[0].options, {'foo': 'bar'})
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_FINDERS=[
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.embedly',
|
||||
}
|
||||
], WAGTAILEMBEDS_EMBEDLY_KEY='bar')
|
||||
def test_new_find_embedly_still_uses_old_key_setting(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], EmbedlyFinder)
|
||||
self.assertEqual(finders[0].get_key(), 'bar')
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_FINDERS=[
|
||||
{
|
||||
'class': 'wagtail.wagtailembeds.finders.embedly',
|
||||
'key': 'foo',
|
||||
}
|
||||
], WAGTAILEMBEDS_EMBEDLY_KEY='bar')
|
||||
def test_new_find_embedly_key_setting_precedence(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], EmbedlyFinder)
|
||||
self.assertEqual(finders[0].get_key(), 'foo')
|
||||
|
||||
# Old settings
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBEDLY_KEY='test')
|
||||
def test_defaults_to_embedly_when_embedly_key_set(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_embedly)
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], EmbedlyFinder)
|
||||
self.assertEqual(finders[0].get_key(), 'test')
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBED_FINDER='wagtail.wagtailembeds.finders.embedly.embedly')
|
||||
def test_find_embedly(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_embedly)
|
||||
def test_old_find_embedly(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], EmbedlyFinder)
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBED_FINDER='wagtail.wagtailembeds.finders.oembed.oembed')
|
||||
def test_find_oembed(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_oembed)
|
||||
def test_old_find_oembed(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], OEmbedFinder)
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBED_FINDER='wagtail.wagtailembeds.finders.embedly')
|
||||
def test_find_embedly_from_module(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_embedly)
|
||||
def test_old_find_embedly_from_module(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], EmbedlyFinder)
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBED_FINDER='wagtail.wagtailembeds.finders.oembed')
|
||||
def test_find_oembed_from_module(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_oembed)
|
||||
def test_old_find_oembed_from_module(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], OEmbedFinder)
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBED_FINDER='wagtail.wagtailembeds.embeds.embedly')
|
||||
def test_find_old_embedly(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_embedly)
|
||||
def test_old_find_old_embedly(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], EmbedlyFinder)
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBED_FINDER='wagtail.wagtailembeds.embeds.oembed')
|
||||
def test_find_old_oembed(self):
|
||||
self.assertEqual(get_default_finder(), wagtail_oembed)
|
||||
def test_old_find_old_oembed(self):
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], OEmbedFinder)
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_EMBEDLY_KEY='test', WAGTAILEMBEDS_EMBED_FINDER='wagtail.wagtailembeds.finders.oembed.oembed')
|
||||
def test_find_oembed_when_embedly_key_set(self):
|
||||
def test_old_find_oembed_when_embedly_key_set(self):
|
||||
# WAGTAILEMBEDS_EMBED_FINDER always takes precedence
|
||||
self.assertEqual(get_default_finder(), wagtail_oembed)
|
||||
finders = get_finders()
|
||||
|
||||
self.assertEqual(len(finders), 1)
|
||||
self.assertIsInstance(finders[0], OEmbedFinder)
|
||||
|
||||
|
||||
class TestEmbeds(TestCase):
|
||||
|
|
@ -145,6 +241,11 @@ class TestEmbeds(TestCase):
|
|||
|
||||
self.assertEqual(embed.html, '')
|
||||
|
||||
@override_settings(WAGTAILEMBEDS_FINDERS=[])
|
||||
def test_no_finders_available(self):
|
||||
with self.assertRaises(EmbedUnsupportedProviderException):
|
||||
get_embed('www.test.com/1234', max_width=400)
|
||||
|
||||
|
||||
class TestChooser(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
|
|
@ -190,10 +291,10 @@ class TestEmbedly(TestCase):
|
|||
oembed.return_value = {'type': 'photo',
|
||||
'url': 'http://www.example.com'}
|
||||
|
||||
wagtail_embedly('http://www.example.com', key='foo')
|
||||
EmbedlyFinder(key='foo').find_embed('http://www.example.com')
|
||||
oembed.assert_called_with('http://www.example.com', better=False)
|
||||
|
||||
wagtail_embedly('http://www.example.com', max_width=100, key='foo')
|
||||
EmbedlyFinder(key='foo').find_embed('http://www.example.com', max_width=100)
|
||||
oembed.assert_called_with('http://www.example.com', maxwidth=100, better=False)
|
||||
|
||||
@unittest.skipIf(no_embedly, "Embedly is not installed")
|
||||
|
|
@ -204,7 +305,7 @@ class TestEmbedly(TestCase):
|
|||
'error': True,
|
||||
'error_code': 401}
|
||||
self.assertRaises(AccessDeniedEmbedlyException,
|
||||
wagtail_embedly, 'http://www.example.com', key='foo')
|
||||
EmbedlyFinder(key='foo').find_embed, 'http://www.example.com')
|
||||
|
||||
@unittest.skipIf(no_embedly, "Embedly is not installed")
|
||||
def test_embedly_403(self):
|
||||
|
|
@ -214,7 +315,7 @@ class TestEmbedly(TestCase):
|
|||
'error': True,
|
||||
'error_code': 403}
|
||||
self.assertRaises(AccessDeniedEmbedlyException,
|
||||
wagtail_embedly, 'http://www.example.com', key='foo')
|
||||
EmbedlyFinder(key='foo').find_embed, 'http://www.example.com')
|
||||
|
||||
@unittest.skipIf(no_embedly, "Embedly is not installed")
|
||||
def test_embedly_404(self):
|
||||
|
|
@ -224,7 +325,7 @@ class TestEmbedly(TestCase):
|
|||
'error': True,
|
||||
'error_code': 404}
|
||||
self.assertRaises(EmbedNotFoundException,
|
||||
wagtail_embedly, 'http://www.example.com', key='foo')
|
||||
EmbedlyFinder(key='foo').find_embed, 'http://www.example.com')
|
||||
|
||||
@unittest.skipIf(no_embedly, "Embedly is not installed")
|
||||
def test_embedly_other_error(self):
|
||||
|
|
@ -233,20 +334,20 @@ class TestEmbedly(TestCase):
|
|||
'url': 'http://www.example.com',
|
||||
'error': True,
|
||||
'error_code': 999}
|
||||
self.assertRaises(EmbedlyException, wagtail_embedly,
|
||||
'http://www.example.com', key='foo')
|
||||
self.assertRaises(EmbedlyException, EmbedlyFinder(key='foo').find_embed,
|
||||
'http://www.example.com')
|
||||
|
||||
@unittest.skipIf(no_embedly, "Embedly is not installed")
|
||||
def test_embedly_html_conversion(self):
|
||||
with patch('embedly.Embedly.oembed') as oembed:
|
||||
oembed.return_value = {'type': 'photo',
|
||||
'url': 'http://www.example.com'}
|
||||
result = wagtail_embedly('http://www.example.com', key='foo')
|
||||
result = EmbedlyFinder(key='foo').find_embed('http://www.example.com')
|
||||
self.assertEqual(result['html'], '<img src="http://www.example.com" />')
|
||||
|
||||
oembed.return_value = {'type': 'something else',
|
||||
'html': '<foo>bar</foo>'}
|
||||
result = wagtail_embedly('http://www.example.com', key='foo')
|
||||
result = EmbedlyFinder(key='foo').find_embed('http://www.example.com')
|
||||
self.assertEqual(result['html'], '<foo>bar</foo>')
|
||||
|
||||
@unittest.skipIf(no_embedly, "Embedly is not installed")
|
||||
|
|
@ -254,7 +355,7 @@ class TestEmbedly(TestCase):
|
|||
with patch('embedly.Embedly.oembed') as oembed:
|
||||
oembed.return_value = {'type': 'something else',
|
||||
'html': '<foo>bar</foo>'}
|
||||
result = wagtail_embedly('http://www.example.com', key='foo')
|
||||
result = EmbedlyFinder(key='foo').find_embed('http://www.example.com')
|
||||
self.assertEqual(result, {
|
||||
'title': '',
|
||||
'author_name': '',
|
||||
|
|
@ -273,7 +374,7 @@ class TestEmbedly(TestCase):
|
|||
'width': 100,
|
||||
'height': 100,
|
||||
'html': '<foo>bar</foo>'}
|
||||
result = wagtail_embedly('http://www.example.com', key='foo')
|
||||
result = EmbedlyFinder(key='foo').find_embed('http://www.example.com')
|
||||
self.assertEqual(result, {'type': 'something else',
|
||||
'author_name': 'Alice',
|
||||
'provider_name': 'Bob',
|
||||
|
|
@ -292,12 +393,12 @@ class TestOembed(TestCase):
|
|||
self.dummy_response = DummyResponse()
|
||||
|
||||
def test_oembed_invalid_provider(self):
|
||||
self.assertRaises(EmbedNotFoundException, wagtail_oembed, "foo")
|
||||
self.assertRaises(EmbedNotFoundException, OEmbedFinder().find_embed, "foo")
|
||||
|
||||
def test_oembed_invalid_request(self):
|
||||
config = {'side_effect': URLError('foo')}
|
||||
with patch.object(django.utils.six.moves.urllib.request, 'urlopen', **config):
|
||||
self.assertRaises(EmbedNotFoundException, wagtail_oembed,
|
||||
self.assertRaises(EmbedNotFoundException, OEmbedFinder().find_embed,
|
||||
"http://www.youtube.com/watch/")
|
||||
|
||||
@patch('django.utils.six.moves.urllib.request.urlopen')
|
||||
|
|
@ -306,7 +407,7 @@ class TestOembed(TestCase):
|
|||
urlopen.return_value = self.dummy_response
|
||||
loads.return_value = {'type': 'photo',
|
||||
'url': 'http://www.example.com'}
|
||||
result = wagtail_oembed("http://www.youtube.com/watch/")
|
||||
result = OEmbedFinder().find_embed("http://www.youtube.com/watch/")
|
||||
self.assertEqual(result['type'], 'photo')
|
||||
self.assertEqual(result['html'], '<img src="http://www.example.com" />')
|
||||
loads.assert_called_with("foo")
|
||||
|
|
@ -326,7 +427,7 @@ class TestOembed(TestCase):
|
|||
'height': 'test_height',
|
||||
'html': 'test_html'
|
||||
}
|
||||
result = wagtail_oembed("http://www.youtube.com/watch/")
|
||||
result = OEmbedFinder().find_embed("http://www.youtube.com/watch/")
|
||||
self.assertEqual(result, {
|
||||
'type': 'something',
|
||||
'title': 'test_title',
|
||||
|
|
@ -338,6 +439,14 @@ class TestOembed(TestCase):
|
|||
'html': 'test_html'
|
||||
})
|
||||
|
||||
def test_oembed_accepts_known_provider(self):
|
||||
finder = OEmbedFinder(providers=[oembed_providers.youtube])
|
||||
self.assertTrue(finder.accept("http://www.youtube.com/watch/"))
|
||||
|
||||
def test_oembed_doesnt_accept_unknown_provider(self):
|
||||
finder = OEmbedFinder(providers=[oembed_providers.twitter])
|
||||
self.assertFalse(finder.accept("http://www.youtube.com/watch/"))
|
||||
|
||||
|
||||
class TestEmbedTag(TestCase):
|
||||
@patch('wagtail.wagtailembeds.embeds.get_embed')
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ from django.forms.utils import ErrorList
|
|||
from django.utils.translation import ugettext as _
|
||||
|
||||
from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
|
||||
from wagtail.wagtailembeds.exceptions import EmbedNotFoundException
|
||||
from wagtail.wagtailembeds.exceptions import (
|
||||
EmbedNotFoundException, EmbedUnsupportedProviderException)
|
||||
from wagtail.wagtailembeds.finders.embedly import AccessDeniedEmbedlyException, EmbedlyException
|
||||
from wagtail.wagtailembeds.format import embed_to_editor_html
|
||||
from wagtail.wagtailembeds.forms import EmbedForm
|
||||
|
|
@ -32,7 +33,7 @@ def chooser_upload(request):
|
|||
)
|
||||
except AccessDeniedEmbedlyException:
|
||||
error = _("There seems to be a problem with your embedly API key. Please check your settings.")
|
||||
except EmbedNotFoundException:
|
||||
except (EmbedNotFoundException, EmbedUnsupportedProviderException):
|
||||
error = _("Cannot find an embed for this URL.")
|
||||
except EmbedlyException:
|
||||
error = _(
|
||||
|
|
|
|||
Loading…
Reference in a new issue