├── oembed
├── __init__.py
├── templatetags
│ ├── __init__.py
│ └── oembed_tags.py
├── views.py
├── templates
│ └── oembed
│ │ ├── link.html
│ │ ├── rich.html
│ │ ├── video.html
│ │ └── photo.html
├── locale
│ └── de
│ │ └── LC_MESSAGES
│ │ ├── django.mo
│ │ └── django.po
├── admin.py
├── models.py
├── tests.py
├── core.py
└── fixtures
│ └── initial_data.json
├── .gitignore
├── tests
├── settings.py
└── runtests.py
├── CONTRIBUTORS.txt
├── MANIFEST.in
├── docs
├── index.txt
├── readme.txt
├── usage.txt
└── installation.txt
├── README.txt
├── setup.py
├── INSTALL.txt
└── LICENSE.txt
/oembed/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/oembed/templatetags/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/oembed/views.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 |
--------------------------------------------------------------------------------
/oembed/templates/oembed/link.html:
--------------------------------------------------------------------------------
1 | {% autoescape off %}{{ response.html }}{% endautoescape %}
--------------------------------------------------------------------------------
/oembed/templates/oembed/rich.html:
--------------------------------------------------------------------------------
1 | {% autoescape off %}{{ response.html }}{% endautoescape %}
--------------------------------------------------------------------------------
/oembed/templates/oembed/video.html:
--------------------------------------------------------------------------------
1 | {% autoescape off %}{{ response.html }}{% endautoescape %}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .DS_Store
3 | *.swp
4 | *.lock
5 | *.pid
6 | *.svn
7 | build
8 | dist
9 | *.egg-info
10 |
--------------------------------------------------------------------------------
/oembed/locale/de/LC_MESSAGES/django.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jezdez/django-oembed/master/oembed/locale/de/LC_MESSAGES/django.mo
--------------------------------------------------------------------------------
/oembed/templates/oembed/photo.html:
--------------------------------------------------------------------------------
1 | {% autoescape off %}
{% endautoescape %}
--------------------------------------------------------------------------------
/tests/settings.py:
--------------------------------------------------------------------------------
1 | DATABASE_ENGINE = 'sqlite3'
2 | ROOT_URLCONF = ''
3 | SITE_ID = 1
4 | INSTALLED_APPS = (
5 | 'oembed',
6 | )
7 |
--------------------------------------------------------------------------------
/oembed/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from oembed.models import ProviderRule, StoredOEmbed
4 |
5 | admin.site.register(ProviderRule)
6 | admin.site.register(StoredOEmbed)
--------------------------------------------------------------------------------
/CONTRIBUTORS.txt:
--------------------------------------------------------------------------------
1 | Eric Florenzano
2 | Idan Gazit
3 | Jannis Leidel
4 | Nathan Borror
5 | Thejaswi Puthraya
6 | Brian Rosner
7 | Jonathan Stasiak
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include CONTRIBUTORS.txt
2 | include INSTALL.txt
3 | include LICENSE.txt
4 | include README.txt
5 | recursive-include docs *.txt
6 | include oembed/fixtures/initial_data.json
7 | recursive-include oembed/locale/* *.po *.mo
8 | recursive-include oembed/templates *.html
9 |
--------------------------------------------------------------------------------
/docs/index.txt:
--------------------------------------------------------------------------------
1 | #############
2 | django-oembed
3 | #############
4 |
5 | Django-oembed is a collection of Django tools which make it easy to change text
6 | filled with oembed links into the embedded objects themselves.
7 |
8 | Contents:
9 |
10 | .. toctree::
11 |
12 | readme.txt
13 | installation.txt
14 | usage.txt
15 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | =======================
2 | django-oembed
3 | =======================
4 |
5 | This is a collection of tools for Django to allow for replacing links in text
6 | with OEmbed. This application also provides utilities to make this process not
7 | prohibitively expensive CPU-wise.
8 |
9 | For installation instructions, read INSTALL.txt.
10 |
11 | Visit the google code page at http://django-oembed.googlecode.com/
--------------------------------------------------------------------------------
/docs/readme.txt:
--------------------------------------------------------------------------------
1 | =======================
2 | django-oembed
3 | =======================
4 |
5 | This is a collection of tools for Django to allow for replacing links in text
6 | with OEmbed. This application also provides utilities to make this process not
7 | prohibitively expensive CPU-wise.
8 |
9 | For installation instructions, read INSTALL.txt.
10 |
11 | Visit the google code page at http://django-oembed.googlecode.com/
--------------------------------------------------------------------------------
/tests/runtests.py:
--------------------------------------------------------------------------------
1 | import sys
2 | sys.path.append('..')
3 |
4 | import os
5 | # Make a backup of DJANGO_SETTINGS_MODULE environment variable to restore later.
6 | backup = os.environ.get('DJANGO_SETTINGS_MODULE', '')
7 | os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
8 |
9 | from django.test.simple import run_tests
10 |
11 | if __name__ == "__main__":
12 | failures = run_tests(['oembed',], verbosity=9)
13 | if failures:
14 | sys.exit(failures)
15 | # Reset the DJANGO_SETTINGS_MODULE to what it was before running tests.
16 | os.environ['DJANGO_SETTINGS_MODULE'] = backup
17 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | setup(
4 | name='django-oembed',
5 | version='0.1.3',
6 | description='A collection of Django tools which make it easy to change'
7 | 'text filled with oembed links into the embedded objects themselves.',
8 | author='Eric Florenzano',
9 | author_email='floguy@gmail.com',
10 | url='http://django-oembed.googlecode.com',
11 | packages=find_packages(),
12 | classifiers=[
13 | 'Development Status :: 3 - Alpha',
14 | 'Environment :: Web Environment',
15 | 'Intended Audience :: Developers',
16 | 'License :: OSI Approved :: BSD License',
17 | 'Operating System :: OS Independent',
18 | 'Programming Language :: Python',
19 | 'Framework :: Django',
20 | ],
21 | include_package_data=True,
22 | zip_safe=False,
23 | )
24 |
--------------------------------------------------------------------------------
/oembed/models.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django.db import models
3 | from django.utils.translation import ugettext_lazy as _
4 |
5 | JSON = 1
6 | XML = 2
7 | FORMAT_CHOICES = (
8 | (JSON, "JSON"),
9 | (XML, "XML"),
10 | )
11 |
12 | class ProviderRule(models.Model):
13 | name = models.CharField(_("name"), max_length=128, null=True, blank=True)
14 | regex = models.CharField(_("regex"), max_length=2000)
15 | endpoint = models.CharField(_("endpoint"), max_length=2000)
16 | format = models.IntegerField(_("format"), choices=FORMAT_CHOICES)
17 |
18 | def __unicode__(self):
19 | return self.name or self.endpoint
20 |
21 |
22 | class StoredOEmbed(models.Model):
23 | match = models.TextField(_("match"))
24 | max_width = models.IntegerField(_("max width"))
25 | max_height = models.IntegerField(_("max height"))
26 | html = models.TextField(_("html"))
27 | date_added = models.DateTimeField(_("date added"), default=datetime.datetime.now)
28 |
29 | def __unicode__(self):
30 | return self.match
31 |
32 |
--------------------------------------------------------------------------------
/docs/usage.txt:
--------------------------------------------------------------------------------
1 | Usage
2 | =====
3 |
4 | First you must add it to your INSTALLED_APPS::
5 |
6 | INSTALLED_APPS = (
7 | ....
8 | 'oembed',
9 | )
10 |
11 | Then in your template, include the oembed tags::
12 |
13 | {% load oembed_tags %}
14 |
15 | Then, surround something with the oembed tag. It will search for oembed-able links and replace them with the proper embed::
16 |
17 | {% oembed %}
18 | There is this great video at http://www.viddler.com/explore/SYSTM/videos/49/
19 | {% endoembed %}
20 |
21 | Will result in::
22 |
23 | There is this great video at
32 |
33 | There is an optional width and height parameter, that can be invoked thusly::
34 |
35 | {% oembed 320x240 %}...{% endoembed %}
--------------------------------------------------------------------------------
/INSTALL.txt:
--------------------------------------------------------------------------------
1 | Thanks for downloading django-oembed!
2 |
3 | To install it, first use subversion to check out the source code:
4 |
5 | svn checkout http://django-oembed.googlecode.com/svn/trunk/ django-oembed
6 |
7 | Now, link the included ``oembed`` directory to your pythonpath. On Debian
8 | variants, it would look something like this.
9 |
10 | sudo ln -s `pwd`/django-oembed/oembed /usr/lib/python2.5/site-packages/
11 |
12 | To use it with a Django installation, first place 'oembed' in the INSTALLED_APPS
13 | tuple of your settings.py file like so:
14 |
15 | INSTALLED_APPS = (
16 | # ...
17 | 'oembed',
18 | )
19 |
20 | Then syncdb, and here is sample usage in a template.
21 |
22 | {% load oembed_tags %}
23 | {% oembed %}
24 | {% for link in links %}{{ link.href }}{% endfor %}
25 | {% endoembed %}
26 |
27 | In the previous example, any link.href would be replaced with an OEmbed-fetched
28 | embed.
29 |
30 | The templatetag takes one optional second argument, which you can figure out by
31 | looking at this usage:
32 |
33 | {% oembed 320x240 %}
34 |
35 | Note that this application requires Python 2.3 or later, and Django later than
36 | 0.96. You can obtain Python from http://www.python.org/ and Django from
37 | http://www.djangoproject.com/.
--------------------------------------------------------------------------------
/oembed/locale/de/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: django-oembed\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2010-04-21 14:32+0200\n"
12 | "PO-Revision-Date: 2010-04-21 14:32+0200\n"
13 | "Last-Translator: Jannis Leidel \n"
14 | "Language-Team: de \n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n != 1)\n"
19 |
20 | #: models.py:13
21 | msgid "name"
22 | msgstr "Name"
23 |
24 | #: models.py:14
25 | msgid "regex"
26 | msgstr "Regulärer Ausdruck"
27 |
28 | #: models.py:15
29 | msgid "endpoint"
30 | msgstr "Endpunkt"
31 |
32 | #: models.py:16
33 | msgid "format"
34 | msgstr "Format"
35 |
36 | #: models.py:23
37 | msgid "match"
38 | msgstr "Match"
39 |
40 | #: models.py:24
41 | msgid "max width"
42 | msgstr "Maximale Breite"
43 |
44 | #: models.py:25
45 | msgid "max height"
46 | msgstr "Maximale Höhe"
47 |
48 | #: models.py:26
49 | msgid "html"
50 | msgstr "HTML-Rohdaten"
51 |
52 | #: models.py:27
53 | msgid "date added"
54 | msgstr "Erstellungsdatum"
55 |
--------------------------------------------------------------------------------
/docs/installation.txt:
--------------------------------------------------------------------------------
1 | Installing django-oembed
2 | ========================
3 |
4 | Thanks for downloading django-oembed!
5 |
6 | To install it, first use subversion to check out the source code::
7 |
8 | svn checkout http://django-oembed.googlecode.com/svn/trunk/ django-oembed
9 |
10 | Now, link the included ``oembed`` directory to your pythonpath. On Debian
11 | variants, it would look something like this::
12 |
13 | sudo ln -s `pwd`/django-oembed/oembed /usr/lib/python2.5/site-packages/
14 |
15 | To use it with a Django installation, first place 'oembed' in the INSTALLED_APPS
16 | tuple of your settings.py file like so::
17 |
18 | INSTALLED_APPS = (
19 | # ...
20 | 'oembed',
21 | )
22 |
23 | Then syncdb, and here is sample usage in a template::
24 |
25 | {% load oembed_tags %}
26 | {% oembed %}
27 | {% for link in links %}{{ link.href }}{% endfor %}
28 | {% endoembed %}
29 |
30 | In the previous example, any link.href would be replaced with an OEmbed-fetched
31 | embed.
32 |
33 | The templatetag takes one optional second argument, which you can figure out by
34 | looking at this usage::
35 |
36 | {% oembed 320x240 %}
37 |
38 | Note that this application requires Python 2.3 or later, and Django later than
39 | 0.96. You can obtain Python from http://www.python.org/ and Django from
40 | http://www.djangoproject.com/.
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2010, Eric Florenzano
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following
12 | disclaimer in the documentation and/or other materials provided
13 | with the distribution.
14 | * Neither the name of the author nor the names of other
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
30 |
--------------------------------------------------------------------------------
/oembed/templatetags/oembed_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.template.defaultfilters import stringfilter
3 | from oembed.core import replace
4 |
5 | register = template.Library()
6 |
7 | def oembed(input, args):
8 | if args:
9 | width, height = args.lower().split('x')
10 | if not width and height:
11 | raise template.TemplateSyntaxError("Oembed's optional WIDTHxHEIGH" \
12 | "T argument requires WIDTH and HEIGHT to be positive integers.")
13 | else:
14 | width, height = None, None
15 | return replace(input, max_width=width, max_height=height)
16 | oembed.is_safe = True
17 | oembed = stringfilter(oembed)
18 |
19 | register.filter('oembed', oembed)
20 |
21 | def do_oembed(parser, token):
22 | """
23 | A node which parses everything between its two nodes, and replaces any links
24 | with OEmbed-provided objects, if possible.
25 |
26 | Supports one optional argument, which is the maximum width and height,
27 | specified like so:
28 |
29 | {% oembed 640x480 %}http://www.viddler.com/explore/SYSTM/videos/49/{% endoembed %}
30 | """
31 | args = token.contents.split()
32 | if len(args) > 2:
33 | raise template.TemplateSyntaxError("Oembed tag takes only one (option" \
34 | "al) argument: WIDTHxHEIGHT, where WIDTH and HEIGHT are positive " \
35 | "integers.")
36 | if len(args) == 2:
37 | width, height = args[1].lower().split('x')
38 | if not width and height:
39 | raise template.TemplateSyntaxError("Oembed's optional WIDTHxHEIGH" \
40 | "T argument requires WIDTH and HEIGHT to be positive integers.")
41 | else:
42 | width, height = None, None
43 | nodelist = parser.parse(('endoembed',))
44 | parser.delete_first_token()
45 | return OEmbedNode(nodelist, width, height)
46 |
47 | register.tag('oembed', do_oembed)
48 |
49 | class OEmbedNode(template.Node):
50 | def __init__(self, nodelist, width, height):
51 | self.nodelist = nodelist
52 | self.width = width
53 | self.height = height
54 |
55 | def render(self, context):
56 | kwargs = {}
57 | if self.width and self.height:
58 | kwargs['max_width'] = self.width
59 | kwargs['max_height'] = self.height
60 | return replace(self.nodelist.render(context), **kwargs)
61 |
--------------------------------------------------------------------------------
/oembed/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from oembed.core import replace
3 |
4 | class OEmbedTests(TestCase):
5 | noembed = ur"This is text that should not match any regex."
6 | end = ur"There is this great video at %s"
7 | start = ur"%s is a video that I like."
8 | middle = ur"There is a movie here: %s and I really like it."
9 | trailing_comma = ur"This is great %s, but it might not work."
10 | trailing_period = ur"I like this video, located at %s."
11 |
12 | locs = [u"http://www.viddler.com/explore/SYSTM/videos/49/",
13 | u"http://www.slideshare.net/hues/easter-plants",
14 | u"http://www.scribd.com/doc/28452730/Easter-Cards",
15 | u"http://screenr.com/gzS",
16 | u"http://www.5min.com/Video/How-to-Decorate-Easter-Eggs-with-Decoupage-142076462",
17 | u"http://www.howcast.com/videos/328008-How-To-Marble-Easter-Eggs",
18 | u"http://my.opera.com/nirvanka/albums/showpic.dml?album=519866&picture=7173711",
19 | u"http://img20.yfrog.com/i/dy6.jpg/",
20 | u"http://tweetphoto.com/8069529",
21 | u"http://www.flickr.com/photos/jaimewalsh/4489497178/",
22 | u"http://twitpic.com/1cm8us",
23 | u"http://imgur.com/6pLoN",
24 | u"http://twitgoo.com/1p94",
25 | u"http://www.23hq.com/Greetingdesignstudio/photo/5464607",
26 | u"http://www.youtube.com/watch?v=Zk7dDekYej0",
27 | u"http://www.veoh.com/browse/videos/category/educational/watch/v7054535EZGFJqyX",
28 | u"http://www.justin.tv/venom24",
29 | u"http://qik.com/video/1445889",
30 | u"http://revision3.com/diggnation/2005-10-06",
31 | u"http://www.dailymotion.com/video/xcss6b_big-cat-easter_animals",
32 | u"http://www.collegehumor.com/video:1682246",
33 | u"http://www.twitvid.com/BC0BA",
34 | u"http://www.break.com/usercontent/2006/11/18/the-evil-easter-bunny-184789",
35 | u"http://vids.myspace.com/index.cfm?fuseaction=vids.individual&videoid=103920940",
36 | u"http://www.metacafe.com/watch/2372088/easter_eggs/",
37 | u"http://blip.tv/file/770127",
38 | u"http://video.google.com/videoplay?docid=2320995867449957036",
39 | u"http://www.revver.com/video/1574939/easter-bunny-house/",
40 | u"http://video.yahoo.com/watch/4530253/12135472",
41 | u"http://www.viddler.com/explore/cheezburger/videos/379/",
42 | u"http://www.liveleak.com/view?i=d91_1239548947",
43 | u"http://www.hulu.com/watch/23349/nova-secrets-of-lost-empires-ii-easter-island",
44 | u"http://movieclips.com/watch/jaws_1975/youre_gonna_need_a_bigger_boat/",
45 | u"http://crackle.com/c/How_To/How_to_Make_Ukraine_Easter_Eggs/2262274",
46 | u"http://www.fancast.com/tv/Saturday-Night-Live/10009/1083396482/Easter-Album/videos",
47 | u"http://www.funnyordie.com/videos/040dac4eff/easter-eggs",
48 | u"http://vimeo.com/10429123",
49 | u"http://www.ted.com/talks/robert_ballard_on_exploring_the_oceans.html",
50 | u"http://www.thedailyshow.com/watch/tue-february-29-2000/headlines---leap-impact",
51 | u"http://www.colbertnation.com/the-colbert-report-videos/181772/march-28-2006/intro---3-28-06",
52 | u"http://www.traileraddict.com/trailer/easter-parade/trailer",
53 | u"http://www.lala.com/#album/432627041169206995/Rihanna/Rated_R",
54 | u"http://www.amazon.com/gp/product/B001EJMS6K/ref=s9_simh_gw_p200_i1?pf_rd_m=ATVPDKIKX0DER",
55 | u"http://animoto.com/s/oH9VwgjOU9hpbgYXNDwLNQ",
56 | u"http://xkcd.com/726/"]
57 |
58 | def get_oembed(self, url):
59 | try:
60 | return replace('%s' % url)
61 | except Exception, e:
62 | self.fail("URL: %s failed for this reason: %s" % (url, str(e)))
63 |
64 | def testNoEmbed(self):
65 | self.assertEquals(
66 | replace(self.noembed),
67 | self.noembed
68 | )
69 |
70 | def testEnd(self):
71 | for loc in self.locs:
72 | embed = self.get_oembed(loc)
73 |
74 | if not embed or embed == loc:
75 | self.fail("URL: %s did not produce an embed object" % loc)
76 |
77 | for text in (self.end, self.start, self.middle, self.trailing_comma, self.trailing_period):
78 | self.assertEquals(
79 | replace(text % loc),
80 | text % embed
81 | )
82 |
83 | def testManySameEmbeds(self):
84 | loc = self.locs[1]
85 | embed = self.get_oembed(loc)
86 |
87 | text = " ".join([self.middle % loc] * 100)
88 | resp = " ".join([self.middle % embed] * 100)
89 | self.assertEquals(replace(text), resp)
--------------------------------------------------------------------------------
/oembed/core.py:
--------------------------------------------------------------------------------
1 | import re
2 | import urllib2
3 | import gzip
4 | from heapq import heappush, heappop
5 | try:
6 | from cStringIO import StringIO
7 | except ImportError:
8 | from StringIO import StringIO
9 | try:
10 | import simplejson
11 | except ImportError:
12 | from django.utils import simplejson
13 | from django.conf import settings
14 | from django.utils.http import urlencode
15 | from django.utils.safestring import mark_safe
16 | from oembed.models import ProviderRule, StoredOEmbed
17 | from django.template.loader import render_to_string
18 | import logging
19 | logger = logging.getLogger("oembed core")
20 |
21 | END_OVERRIDES = (')', ',', '.', '>', ']', ';')
22 | MAX_WIDTH = getattr(settings, "OEMBED_MAX_WIDTH", 320)
23 | MAX_HEIGHT = getattr(settings, "OEMBED_MAX_HEIGHT", 240)
24 | FORMAT = getattr(settings, "OEMBED_FORMAT", "json")
25 |
26 | def fetch(url, user_agent="django-oembed/0.1"):
27 | """
28 | Fetches from a URL, respecting GZip encoding, etc.
29 | """
30 | request = urllib2.Request(url)
31 | request.add_header('User-Agent', user_agent)
32 | request.add_header('Accept-Encoding', 'gzip')
33 | opener = urllib2.build_opener()
34 | f = opener.open(request)
35 | result = f.read()
36 | if f.headers.get('content-encoding', '') == 'gzip':
37 | result = gzip.GzipFile(fileobj=StringIO(result)).read()
38 | f.close()
39 | return result
40 |
41 | def re_parts(regex_list, text):
42 | """
43 | An iterator that returns the entire text, but split by which regex it
44 | matched, or none at all. If it did, the first value of the returned tuple
45 | is the index into the regex list, otherwise -1.
46 |
47 | >>> first_re = re.compile('asdf')
48 | >>> second_re = re.compile('an')
49 | >>> list(re_parts([first_re, second_re], 'This is an asdf test.'))
50 | [(-1, 'This is '), (1, 'an'), (-1, ' '), (0, 'asdf'), (-1, ' test.')]
51 |
52 | >>> list(re_parts([first_re, second_re], 'asdfasdfasdf'))
53 | [(0, 'asdf'), (0, 'asdf'), (0, 'asdf')]
54 |
55 | >>> list(re_parts([], 'This is an asdf test.'))
56 | [(-1, 'This is an asdf test.')]
57 |
58 | >>> third_re = re.compile('sdf')
59 | >>> list(re_parts([first_re, second_re, third_re], 'This is an asdf test.'))
60 | [(-1, 'This is '), (1, 'an'), (-1, ' '), (0, 'asdf'), (-1, ' test.')]
61 | """
62 | def match_compare(x, y):
63 | return x.start() - y.start()
64 | prev_end = 0
65 | iter_dict = dict((r, r.finditer(text)) for r in regex_list)
66 |
67 | # a heapq containing matches
68 | matches = []
69 |
70 | # bootstrap the search with the first hit for each iterator
71 | for regex, iterator in iter_dict.items():
72 | try:
73 | match = iterator.next()
74 | heappush(matches, (match.start(), match))
75 | except StopIteration:
76 | iter_dict.pop(regex)
77 |
78 | # process matches, revisiting each iterator from which a match is used
79 | while matches:
80 | # get the earliest match
81 | start, match = heappop(matches)
82 | end = match.end()
83 | if start > prev_end:
84 | # yield the text from current location to start of match
85 | yield (-1, text[prev_end:start])
86 | # yield the match
87 | yield (regex_list.index(match.re), text[start:end])
88 | # get the next match from the iterator for this match
89 | if match.re in iter_dict:
90 | try:
91 | newmatch = iter_dict[match.re].next()
92 | heappush(matches, (newmatch.start(), newmatch))
93 | except StopIteration:
94 | iter_dict.pop(match.re)
95 | prev_end = end
96 |
97 | # yield text from end of last match to end of text
98 | last_bit = text[prev_end:]
99 | if len(last_bit) > 0:
100 | yield (-1, last_bit)
101 |
102 | def replace(text, max_width=MAX_WIDTH, max_height=MAX_HEIGHT):
103 | """
104 | Scans a block of text, replacing anything matched by a ``ProviderRule``
105 | pattern with an OEmbed html snippet, if possible.
106 |
107 | Templates should be stored at oembed/{format}.html, so for example:
108 |
109 | oembed/video.html
110 |
111 | These templates are passed a context variable, ``response``, which is a
112 | dictionary representation of the response.
113 | """
114 | rules = list(ProviderRule.objects.all())
115 | patterns = [re.compile(r.regex, re.I) for r in rules] # Compiled patterns from the rules
116 | parts = [] # The parts that we will assemble into the final return value.
117 | indices = [] # List of indices of parts that need to be replaced with OEmbed stuff.
118 | indices_rules = [] # List of indices into the rules in order for which index was gotten by.
119 | urls = set() # A set of URLs to try to lookup from the database.
120 | stored = {} # A mapping of URLs to StoredOEmbed objects.
121 | index = 0
122 | # First we pass through the text, populating our data structures.
123 | for i, part in re_parts(patterns, text):
124 | if i == -1:
125 | parts.append(part)
126 | index += 1
127 | else:
128 | to_append = ""
129 | # If the link ends with one of our overrides, build a list
130 | while part[-1] in END_OVERRIDES:
131 | to_append += part[-1]
132 | part = part[:-1]
133 | indices.append(index)
134 | urls.add(part)
135 | indices_rules.append(i)
136 | parts.append(part)
137 | index += 1
138 | if to_append:
139 | parts.append(to_append)
140 | index += 1
141 | # Now we fetch a list of all stored patterns, and put it in a dictionary
142 | # mapping the URL to to the stored model instance.
143 | for stored_embed in StoredOEmbed.objects.filter(match__in=urls, max_width=max_width, max_height = max_height):
144 | stored[stored_embed.match] = stored_embed
145 | # Now we're going to do the actual replacement of URL to embed.
146 | for i, id_to_replace in enumerate(indices):
147 | rule = rules[indices_rules[i]]
148 | part = parts[id_to_replace]
149 | try:
150 | # Try to grab the stored model instance from our dictionary, and
151 | # use the stored HTML fragment as a replacement.
152 | parts[id_to_replace] = stored[part].html
153 | except KeyError:
154 | try:
155 | # Build the URL based on the properties defined in the OEmbed spec.
156 | sep = "?" in rule.endpoint and "&" or "?"
157 | q = urlencode({"url": part,
158 | "maxwidth": max_width,
159 | "maxheight": max_height,
160 | "format": FORMAT})
161 | url = u"%s%s%s" % (rule.endpoint, sep, q)
162 | # Fetch the link and parse the JSON.
163 | resp = simplejson.loads(fetch(url))
164 | # Depending on the embed type, grab the associated template and
165 | # pass it the parsed JSON response as context.
166 | replacement = render_to_string('oembed/%s.html' % resp['type'], {'response': resp})
167 | if replacement:
168 | stored_embed = StoredOEmbed.objects.create(
169 | match = part,
170 | max_width = max_width,
171 | max_height = max_height,
172 | html = replacement,
173 | )
174 | stored[stored_embed.match] = stored_embed
175 | parts[id_to_replace] = replacement
176 | else:
177 | raise ValueError
178 | except ValueError:
179 | parts[id_to_replace] = part
180 | except KeyError:
181 | parts[id_to_replace] = part
182 | except urllib2.HTTPError:
183 | parts[id_to_replace] = part
184 | # Combine the list into one string and return it.
185 | return mark_safe(u''.join(parts))
186 |
--------------------------------------------------------------------------------
/oembed/fixtures/initial_data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pk": 1,
4 | "model": "oembed.providerrule",
5 | "fields": {
6 | "regex": "http://\\S*?flickr.com/\\S*",
7 | "endpoint": "http://www.flickr.com/services/oembed/",
8 | "name": "Flickr",
9 | "format": 1
10 | }
11 | },
12 | {
13 | "pk": 2,
14 | "model": "oembed.providerrule",
15 | "fields": {
16 | "regex": "http://\\S*?viddler.com/\\S*",
17 | "endpoint": "http://lab.viddler.com/services/oembed/",
18 | "name": "Viddler",
19 | "format": 1
20 | }
21 | },
22 | {
23 | "pk": 3,
24 | "model": "oembed.providerrule",
25 | "fields": {
26 | "regex": "http://qik.com/\\S*",
27 | "endpoint": "http://qik.com/api/oembed.json",
28 | "name": "Qik",
29 | "format": 1
30 | }
31 | },
32 | {
33 | "pk": 4,
34 | "model": "oembed.providerrule",
35 | "fields": {
36 | "regex": "http://\\S*?pownce.com/\\S*",
37 | "endpoint": "http://api.pownce.com/2.1/oembed.json",
38 | "name": "Pownce",
39 | "format": 1
40 | }
41 | },
42 | {
43 | "pk": 5,
44 | "model": "oembed.providerrule",
45 | "fields": {
46 | "regex": "http://\\S*?revision3.com/\\S*",
47 | "endpoint": "http://revision3.com/api/oembed/",
48 | "name": "Revision3",
49 | "format": 1
50 | }
51 | },
52 | {
53 | "pk": 6,
54 | "model": "oembed.providerrule",
55 | "fields": {
56 | "regex": "http://\\S*.collegehumor.com/video:\\S*",
57 | "endpoint": "http://oohembed.com/oohembed/",
58 | "name": "CollegeHumor Video (OohEmbed)",
59 | "format": 1
60 | }
61 | },
62 | {
63 | "pk": 7,
64 | "model": "oembed.providerrule",
65 | "fields": {
66 | "regex": "http://\\S*.funnyordie.com/videos/\\S*",
67 | "endpoint": "http://oohembed.com/oohembed/",
68 | "name": "Funny or Die Video (OohEmbed)",
69 | "format": 1
70 | }
71 | },
72 | {
73 | "pk": 8,
74 | "model": "oembed.providerrule",
75 | "fields": {
76 | "regex": "http://video.google.com/videoplay?\\S*",
77 | "endpoint": "http://oohembed.com/oohembed/",
78 | "name": "Google Video (OohEmbed)",
79 | "format": 1
80 | }
81 | },
82 | {
83 | "pk": 9,
84 | "model": "oembed.providerrule",
85 | "fields": {
86 | "regex": "http://www.hulu.com/watch/\\S*",
87 | "endpoint": "http://oohembed.com/oohembed/",
88 | "name": "Hulu (OohEmbed)",
89 | "format": 1
90 | }
91 | },
92 | {
93 | "pk": 10,
94 | "model": "oembed.providerrule",
95 | "fields": {
96 | "regex": "http://\\S*.metacafe.com/watch/\\S*",
97 | "endpoint": "http://oohembed.com/oohembed/",
98 | "name": "Metacafe (OohEmbed)",
99 | "format": 1
100 | }
101 | },
102 | {
103 | "pk": 11,
104 | "model": "oembed.providerrule",
105 | "fields": {
106 | "regex": "http://twitter.com/\\S*/statuses/\\S*",
107 | "endpoint": "http://oohembed.com/oohembed/",
108 | "name": "Twitter Status (OohEmbed)",
109 | "format": 1
110 | }
111 | },
112 | {
113 | "pk": 12,
114 | "model": "oembed.providerrule",
115 | "fields": {
116 | "regex": "http://\\S*.wikipedia.org/wiki/\\S*",
117 | "endpoint": "http://oohembed.com/oohembed/",
118 | "name": "Wikipedia (OohEmbed)",
119 | "format": 1
120 | }
121 | },
122 | {
123 | "pk": 13,
124 | "model": "oembed.providerrule",
125 | "fields": {
126 | "regex": "http://\\S*.youtube.com/watch\\S*",
127 | "endpoint": "http://www.youtube.com/oembed",
128 | "name": "YouTube",
129 | "format": 1
130 | }
131 | },
132 | {
133 | "pk": 14,
134 | "model": "oembed.providerrule",
135 | "fields": {
136 | "regex": "http://vimeo.com/\\S*",
137 | "endpoint": "http://vimeo.com/api/oembed.json",
138 | "name": "Vimeo",
139 | "format": 1
140 | }
141 | },
142 | {
143 | "pk": 15,
144 | "model": "oembed.providerrule",
145 | "fields": {
146 | "regex": "http://www.slideshare.net/\\S*/\\S*",
147 | "endpoint": "http://api.embed.ly/v1/api/oembed",
148 | "name": "SlideShare (Embedly)",
149 | "format": 1
150 | }
151 | },
152 | {
153 | "pk": 16,
154 | "model": "oembed.providerrule",
155 | "fields": {
156 | "regex": "http://\\S*.scribd.com/doc/\\S*",
157 | "endpoint": "http://api.embed.ly/v1/api/oembed",
158 | "name": "Scribd (Embedly)",
159 | "format": 1
160 | }
161 | },
162 | {
163 | "pk": 17,
164 | "model": "oembed.providerrule",
165 | "fields": {
166 | "regex": "http://screenr.com/\\S*",
167 | "endpoint": "http://api.embed.ly/v1/api/oembed",
168 | "name": "Screenr (Embedly)",
169 | "format": 1
170 | }
171 | },
172 | {
173 | "pk": 18,
174 | "model": "oembed.providerrule",
175 | "fields": {
176 | "regex": "http://www.5min.com/Video/\\S*",
177 | "endpoint": "http://api.embed.ly/v1/api/oembed",
178 | "name": "5min (Embedly)",
179 | "format": 1
180 | }
181 | },
182 | {
183 | "pk": 19,
184 | "model": "oembed.providerrule",
185 | "fields": {
186 | "regex": "http://www.howcast.com/videos/\\S*",
187 | "endpoint": "http://api.embed.ly/v1/api/oembed",
188 | "name": "Howcast (Embedly)",
189 | "format": 1
190 | }
191 | },
192 | {
193 | "pk": 20,
194 | "model": "oembed.providerrule",
195 | "fields": {
196 | "regex": "(http://\\S*?screencast.com/\\S*/media/\\S*|http://\\S*?screencast.com/t/\\S*)",
197 | "endpoint": "http://api.embed.ly/v1/api/oembed",
198 | "name": "Screencast (Embedly)",
199 | "format": 1
200 | }
201 | },
202 | {
203 | "pk": 21,
204 | "model": "oembed.providerrule",
205 | "fields": {
206 | "regex": "http://www.clearspring.com/widgets/\\S*",
207 | "endpoint": "http://api.embed.ly/v1/api/oembed",
208 | "name": "Clearspring (Embedly)",
209 | "format": 1
210 | }
211 | },
212 | {
213 | "pk": 22,
214 | "model": "oembed.providerrule",
215 | "fields": {
216 | "regex": "(http://my.opera.com/\\S*/albums/show.dml\\?id=\\S*|http://my.opera.com/\\S*/albums/showpic.dml\\?album=\\S*&picture=\\S*)",
217 | "endpoint": "http://api.embed.ly/v1/api/oembed",
218 | "name": "My Opera (Embedly)",
219 | "format": 1
220 | }
221 | },
222 | {
223 | "pk": 23,
224 | "model": "oembed.providerrule",
225 | "fields": {
226 | "regex": "http://\\S*yfrog.\\S*/\\S*",
227 | "endpoint": "http://api.embed.ly/v1/api/oembed",
228 | "name": "Yfrog (Embedly)",
229 | "format": 1
230 | }
231 | },
232 | {
233 | "pk": 24,
234 | "model": "oembed.providerrule",
235 | "fields": {
236 | "regex": "http://tweetphoto.com/\\S*",
237 | "endpoint": "http://api.embed.ly/v1/api/oembed",
238 | "name": "TweetPhoto (Embedly)",
239 | "format": 1
240 | }
241 | },
242 | {
243 | "pk": 25,
244 | "model": "oembed.providerrule",
245 | "fields": {
246 | "regex": "http://\\S*twitpic.com/\\S*",
247 | "endpoint": "http://api.embed.ly/v1/api/oembed",
248 | "name": "TwitPic (Embedly)",
249 | "format": 1
250 | }
251 | },
252 | {
253 | "pk": 26,
254 | "model": "oembed.providerrule",
255 | "fields": {
256 | "regex": "http://\\S*imgur.com/\\S*",
257 | "endpoint": "http://api.embed.ly/v1/api/oembed",
258 | "name": "Imgur (Embedly)",
259 | "format": 1
260 | }
261 | },
262 | {
263 | "pk": 27,
264 | "model": "oembed.providerrule",
265 | "fields": {
266 | "regex": "http://twitgoo.com/\\S*",
267 | "endpoint": "http://api.embed.ly/v1/api/oembed",
268 | "name": "TwitGoo (Embedly)",
269 | "format": 1
270 | }
271 | },
272 | {
273 | "pk": 28,
274 | "model": "oembed.providerrule",
275 | "fields": {
276 | "regex": "(http://i\\S*.photobucket.com/albums/\\S*|http://gi\\S*.photobucket.com/groups/\\S*)",
277 | "endpoint": "http://api.embed.ly/v1/api/oembed",
278 | "name": "Photobucket (Embedly)",
279 | "format": 1
280 | }
281 | },
282 | {
283 | "pk": 29,
284 | "model": "oembed.providerrule",
285 | "fields": {
286 | "regex": "http://phodroid.com/\\S*/\\S*/\\S*",
287 | "endpoint": "http://api.embed.ly/v1/api/oembed",
288 | "name": "Phodroid (Embedly)",
289 | "format": 1
290 | }
291 | },
292 | {
293 | "pk": 30,
294 | "model": "oembed.providerrule",
295 | "fields": {
296 | "regex": "http://xkcd.com/\\S*",
297 | "endpoint": "http://api.embed.ly/v1/api/oembed",
298 | "name": "xkcd (Embedly)",
299 | "format": 1
300 | }
301 | },
302 | {
303 | "pk": 31,
304 | "model": "oembed.providerrule",
305 | "fields": {
306 | "regex": "http://\\S*?23hq.com/\\S*/photo/\\S*",
307 | "endpoint": "http://api.embed.ly/v1/api/oembed",
308 | "name": "23 HQ (Embedly)",
309 | "format": 1
310 | }
311 | },
312 | {
313 | "pk": 32,
314 | "model": "oembed.providerrule",
315 | "fields": {
316 | "regex": "(http://\\S*amazon.\\S*/gp/product/\\S*|http://\\S*amazon.\\S*/\\S*/dp/\\S*|http://\\S*amazon.\\S*/dp/\\S*|http://\\S*amazon.\\S*/o/ASIN/\\S*|http://\\S*amazon.\\S*/gp/offer-listing/\\S*|http://\\S*amazon.\\S*/\\S*/ASIN/\\S*|http://\\S*amazon.\\S*/gp/product/images/\\S*)",
317 | "endpoint": "http://api.embed.ly/v1/api/oembed",
318 | "name": "Amazon (Embedly)",
319 | "format": 1
320 | }
321 | },
322 | {
323 | "pk": 33,
324 | "model": "oembed.providerrule",
325 | "fields": {
326 | "regex": "http://www.veoh.com/\\S*/watch/\\S*",
327 | "endpoint": "http://api.embed.ly/v1/api/oembed",
328 | "name": "Veoh (Embedly)",
329 | "format": 1
330 | }
331 | },
332 | {
333 | "pk": 34,
334 | "model": "oembed.providerrule",
335 | "fields": {
336 | "regex": "http://\\S*justin.tv/\\S*",
337 | "endpoint": "http://api.embed.ly/v1/api/oembed",
338 | "name": "Justin.tv (Embedly)",
339 | "format": 1
340 | }
341 | },
342 | {
343 | "pk": 35,
344 | "model": "oembed.providerrule",
345 | "fields": {
346 | "regex": "http://www.ustream.tv/(recorded|channel)/\\S*",
347 | "endpoint": "http://api.embed.ly/v1/api/oembed",
348 | "name": "UStream (Embedly)",
349 | "format": 1
350 | }
351 | },
352 | {
353 | "pk": 36,
354 | "model": "oembed.providerrule",
355 | "fields": {
356 | "regex": "(http://\\S*.dailymotion.com/video/\\S*|http://\\S*.dailymotion.com/\\S*/video/\\S*)",
357 | "endpoint": "http://api.embed.ly/v1/api/oembed",
358 | "name": "Daily Motion (Embedly)",
359 | "format": 1
360 | }
361 | },
362 | {
363 | "pk": 37,
364 | "model": "oembed.providerrule",
365 | "fields": {
366 | "regex": "http://www.twitvid.com/\\S*",
367 | "endpoint": "http://api.embed.ly/v1/api/oembed",
368 | "name": "TwitVid (Embedly)",
369 | "format": 1
370 | }
371 | },
372 | {
373 | "pk": 38,
374 | "model": "oembed.providerrule",
375 | "fields": {
376 | "regex": "http://www.break.com/\\S*/\\S*",
377 | "endpoint": "http://api.embed.ly/v1/api/oembed",
378 | "name": "Break.com (Embedly)",
379 | "format": 1
380 | }
381 | },
382 | {
383 | "pk": 39,
384 | "model": "oembed.providerrule",
385 | "fields": {
386 | "regex": "http://(www|vids).myspace.com/index.cfm\\?fuseaction=\\S*&videoid\\S*",
387 | "endpoint": "http://api.embed.ly/v1/api/oembed",
388 | "name": "Myspace Videos (Embedly)",
389 | "format": 1
390 | }
391 | },
392 | {
393 | "pk": 40,
394 | "model": "oembed.providerrule",
395 | "fields": {
396 | "regex": "http://\\S*blip.tv/file/\\S*",
397 | "endpoint": "http://api.embed.ly/v1/api/oembed",
398 | "name": "Blip.tv (Embedly)",
399 | "format": 1
400 | }
401 | },
402 | {
403 | "pk": 41,
404 | "model": "oembed.providerrule",
405 | "fields": {
406 | "regex": "http://\\S*revver.com/video/\\S*",
407 | "endpoint": "http://api.embed.ly/v1/api/oembed",
408 | "name": "Revver (Embedly)",
409 | "format": 1
410 | }
411 | },
412 | {
413 | "pk": 42,
414 | "model": "oembed.providerrule",
415 | "fields": {
416 | "regex": "(http://video.yahoo.com/watch/\\S*/\\S*|http://video.yahoo.com/network/\\S*)",
417 | "endpoint": "http://api.embed.ly/v1/api/oembed",
418 | "name": "Yahoo! Video (Embedly)",
419 | "format": 1
420 | }
421 | },
422 | {
423 | "pk": 43,
424 | "model": "oembed.providerrule",
425 | "fields": {
426 | "regex": "http://\\S*?liveleak.com/view?\\S*",
427 | "endpoint": "http://api.embed.ly/v1/api/oembed",
428 | "name": "LiveLeak (Embedly)",
429 | "format": 1
430 | }
431 | },
432 | {
433 | "pk": 44,
434 | "model": "oembed.providerrule",
435 | "fields": {
436 | "regex": "http://animoto.com/(play|s)/\\S*",
437 | "endpoint": "http://api.embed.ly/v1/api/oembed",
438 | "name": "Animoto (Embedly)",
439 | "format": 1
440 | }
441 | },
442 | {
443 | "pk": 45,
444 | "model": "oembed.providerrule",
445 | "fields": {
446 | "regex": "http://dotsub.com/view/\\S*",
447 | "endpoint": "http://api.embed.ly/v1/api/oembed",
448 | "name": "dotSUB (Embedly)",
449 | "format": 1
450 | }
451 | },
452 | {
453 | "pk": 46,
454 | "model": "oembed.providerrule",
455 | "fields": {
456 | "regex": "http://soundcloud.com/\\S*",
457 | "endpoint": "http://api.embed.ly/v1/api/oembed",
458 | "name": "SoundCloud (Embedly)",
459 | "format": 1
460 | }
461 | },
462 | {
463 | "pk": 47,
464 | "model": "oembed.providerrule",
465 | "fields": {
466 | "regex": "http://www.lala.com/#*(album|song)/\\S*",
467 | "endpoint": "http://api.embed.ly/v1/api/oembed",
468 | "name": "Lala (Embedly)",
469 | "format": 1
470 | }
471 | },
472 | {
473 | "pk": 48,
474 | "model": "oembed.providerrule",
475 | "fields": {
476 | "regex": "(http://movieclips.com/watch/\\S*/\\S*/|http://movieclips.com/watch/\\S*/\\S*/\\S*/\\S*)",
477 | "endpoint": "http://api.embed.ly/v1/api/oembed",
478 | "name": "Movie Clips (Embedly)",
479 | "format": 1
480 | }
481 | },
482 | {
483 | "pk": 49,
484 | "model": "oembed.providerrule",
485 | "fields": {
486 | "regex": "http://\\S*crackle.com/c/\\S*",
487 | "endpoint": "http://api.embed.ly/v1/api/oembed",
488 | "name": "Crackle (Embedly)",
489 | "format": 1
490 | }
491 | },
492 | {
493 | "pk": 50,
494 | "model": "oembed.providerrule",
495 | "fields": {
496 | "regex": "http://www.fancast.com/\\S*/videos",
497 | "endpoint": "http://api.embed.ly/v1/api/oembed",
498 | "name": "Fancast (Embedly)",
499 | "format": 1
500 | }
501 | },
502 | {
503 | "pk": 51,
504 | "model": "oembed.providerrule",
505 | "fields": {
506 | "regex": "http://www.ted.com/talks/\\S*.html",
507 | "endpoint": "http://api.embed.ly/v1/api/oembed",
508 | "name": "TED (Embedly)",
509 | "format": 1
510 | }
511 | },
512 | {
513 | "pk": 52,
514 | "model": "oembed.providerrule",
515 | "fields": {
516 | "regex": "http://\\S*omnisio.com/\\S*",
517 | "endpoint": "http://api.embed.ly/v1/api/oembed",
518 | "name": "Omnisio (Embedly)",
519 | "format": 1
520 | }
521 | },
522 | {
523 | "pk": 53,
524 | "model": "oembed.providerrule",
525 | "fields": {
526 | "regex": "http://\\S*nfb.ca/film/\\S*",
527 | "endpoint": "http://api.embed.ly/v1/api/oembed",
528 | "name": "NFB (Embedly)",
529 | "format": 1
530 | }
531 | },
532 | {
533 | "pk": 54,
534 | "model": "oembed.providerrule",
535 | "fields": {
536 | "regex": "(http://www.thedailyshow.com/(watch|full-episodes)/\\S*|http://www.thedailyshow.com/collection/\\S*/\\S*/\\S*)",
537 | "endpoint": "http://api.embed.ly/v1/api/oembed",
538 | "name": "The Daily Show (Embedly)",
539 | "format": 1
540 | }
541 | },
542 | {
543 | "pk": 55,
544 | "model": "oembed.providerrule",
545 | "fields": {
546 | "regex": "http://movies.yahoo.com/movie/\\S*/(video|info|trailer)/\\S*",
547 | "endpoint": "http://api.embed.ly/v1/api/oembed",
548 | "name": "Yahoo! Movies",
549 | "format": 1
550 | }
551 | },
552 | {
553 | "pk": 56,
554 | "model": "oembed.providerrule",
555 | "fields": {
556 | "regex": "http://www.colbertnation.com/(the-colbert-report-collections|full-episodes|the-colbert-report-videos)/\\S*",
557 | "endpoint": "http://api.embed.ly/v1/api/oembed",
558 | "name": "Colbert Nation (Embedly)",
559 | "format": 1
560 | }
561 | },
562 | {
563 | "pk": 57,
564 | "model": "oembed.providerrule",
565 | "fields": {
566 | "regex": "http://www.comedycentral.com/videos/index.jhtml?\\S*",
567 | "endpoint": "http://api.embed.ly/v1/api/oembed",
568 | "name": "Comedy Central (Embedly)",
569 | "format": 1
570 | }
571 | },
572 | {
573 | "pk": 58,
574 | "model": "oembed.providerrule",
575 | "fields": {
576 | "regex": "http://\\S*theonion.com/video/\\S*",
577 | "endpoint": "http://api.embed.ly/v1/api/oembed",
578 | "name": "The Onion (Embedly)",
579 | "format": 1
580 | }
581 | },
582 | {
583 | "pk": 59,
584 | "model": "oembed.providerrule",
585 | "fields": {
586 | "regex": "http://wordpress.tv/\\S*/\\S*/\\S*/\\S*/",
587 | "endpoint": "http://api.embed.ly/v1/api/oembed",
588 | "name": "WordPress TV (Embedly)",
589 | "format": 1
590 | }
591 | },
592 | {
593 | "pk": 60,
594 | "model": "oembed.providerrule",
595 | "fields": {
596 | "regex": "http://www.traileraddict.com/(trailer|clip|poster)/\\S*",
597 | "endpoint": "http://api.embed.ly/v1/api/oembed",
598 | "name": "Trailer Addict (Embedly)",
599 | "format": 1
600 | }
601 | },
602 | {
603 | "pk": 61,
604 | "model": "oembed.providerrule",
605 | "fields": {
606 | "regex": "http://www.escapistmagazine.com/videos/\\S*",
607 | "endpoint": "http://api.embed.ly/v1/api/oembed",
608 | "name": "The Escapist (Embedly)",
609 | "format": 1
610 | }
611 | }
612 | ]
613 |
--------------------------------------------------------------------------------