I'm a big fan of markdown, especially the fact that it can be extended so easily. I wanted to give the users of DeathCat Inc. the ability to embed video from popular services in their posts with only an URL, so I wrote up a new extension. To see it in action, go here. This code is licensed under LGPL.

You can get it here.

It is installable as many other python modules...

python setup.py install

..., but mdx_video.py only has to be in your PYTHON_PATH. With Django, for instance, you can place it in the same directory as settings.py.

Using my extension is like using any other extension for markdown.

>>> import markdown
>>> s = "http://www.youtube.com/watch?v=F8qwxzQar2g"
>>> markdown.markdown(s, ['video'], safe_mode='escape')

Remember to set safe_mode to escape if you are passing untrusted users' input through markdown, extension or not. This extension supports arguments for setting the dimension of the video. By default, the dimensions are what the specific service gives for their example embed. If you don't like this size, then you can change it like so:

>>> markdown.markdown(s, ['video(youtube_width=720, youtube_height=400)'], safe_mode='escape')

If you want to integrate markdown and this extension with Django, then I recommend looking at this post.

This extension supports the following services:

  • Blip.tv
  • Dailymotion
  • Metacafe
  • Veoh
  • Vimeo
  • Yahoo! video
  • Youtube

NOTE: Blip.tv works a little differently than the others because there is no way to construct a working object with the player URL. Instead of the URL to the Blip.tv page, you will use the URL to the flv file, like http://blip.tv/file/get/Pycon-DjangoOnJython531.flv for example. This is located in Files and Links section of Blip.tv.

Adding extra services is easy. Note: This portion is relevant to the extension for python-markdown 1.7. The first part is defining what URL for the video service should look like. You do this in the extendMarkdown method of VideoExtension.

# This regular expression looks for a youtube URL that do not start with parenthesis.
# It does this to avoid eating regular markdown links.
YOUTUBE_RE = r'([^(]|^)http://www\.youtube\.com/watch\?\S*v=(?P<youtubeid>[A-Za-z0-9_-]+)\S*'
# Here we plug our expression into the bit that builds the video embed html.
# We define this shortly.
# The next two lines allow control of markdown through instances of this class.
# This registers everything with markdown, so the code will be executed.

Next, we get to build the HTML. We need to add a new subclass of markdown.BasePattern to the module.

class Youtube(markdown.BasePattern):
    def handleMatch(self, m, doc):
        url = 'http://www.youtube.com/v/%s' % m.group('youtubeid')
        width = self.ext.getConfig('youtube_width')
        height = self.ext.getConfig('youtube_height')
        return FlashObject(doc, url, width, height)

For the most part, building the HTML is easy. I have defined a flash_object function that builds an object element that work in most cases. You only need to feed it your minidom instance, doc, an url, and width/height, both as strings. You will notice that I am using self.ext.getConfig to assign my width and height. These are the extension arguments. You will want to use these too. To do so, add a new key to the self.config dictionary of __init__ in VideoExtension.

'youtube_width': ['640', 'Width for Youtube videos'],
'youtube_height': ['385', 'Height for Youtube videos'],

The first part is the default value and the second bit is a description of the argument. If you need to define more HTML, then I suggest taking look at the Yahoo class.

Posted by Tyler Lesmann on April 2, 2009 at 8:16
Tagged as: django markdown open_source python
#1 Waylan Limberg wrote this 8 years, 8 months ago

Very cool Tyler. I just posted a link from the wiki.

However, this coming weekend, we will be releasing version 2.0 and your extension will no longer work. The extension API will be changing ever to slightly. You thought the API was powerful now, wait till to see what we have coming. Unfortunately, that means most older extensions will need upgrading.

Check out what's coming in our Git repo (including docs) here: <http://gitorious.org/projects/python-markdown/repos/mainline>

#2 Tyler Lesmann wrote this 8 years, 8 months ago

Thanks for the heads up and the link. I can't wait to see the changes. :)

#3 Damon wrote this 8 years, 3 months ago

Hi - this is great, btw, thank you! I am having trouble, however, trying to use this as a template tag. It works great, as such:

{{ object.url|markdown:'video' }}

This does behave as expected; however, I can't pass it any height/width variables ... for example:

{{ object.url|markdown:'video(youtube_height=385,youtube_width=640)' }}

That doesn't work for me. I think the comma is causing some problems because if I pass one variable (only) it does work (sort of, it cuts off the last digit).

{{ object.url|markdown:'video(youtube_height=385)' }}

I tried escaping the comma (\,) and that at least passes the first variable correctly, but the second won't got through. Any ideas ?

#4 Tyler Lesmann wrote this 8 years, 3 months ago

This is bizarre. I experimented with the RSS extension, one of the few that takes multiple arguments, and it exhibits the same behavior. It seems like django.contrib.markup doesn't work in this use case. I had a similar problem, now that I think about it.

The quick fix would be to alter the extension to have your proper sizes.

The best fix would be for you to move this code out of the template. I recommend parsing the text at Model.save and saving the html to the database. You'll get the best performance with this method.

#5 Damon wrote this 8 years, 3 months ago

Hmm - I wonder if it is something a bug report at django couldn't fix ?

I see how it would be more "expensive" to run this in the template than in the Model.save() area. Interesting idea -- I was hoping that I could separate the decision about the video width/height from the model layer. Then, during the design/html/css process it's a small switch to flip in the template ... rather than futzing with the model.

But you raise a good point. Thanks again.

#6 benjamin bach wrote this 7 years, 10 months ago

awesome! i'm including this in the simple wiki project!


#7 Tyler Lesmann wrote this 7 years, 10 months ago

Glad I could make a great project better! :)

#8 airtonix wrote this 6 years, 10 months ago

it seems you would need to update this post with some minor corrections, as your source code repository shows something slightly different than what you show here.

Nothing major, but enough to throw new comers off.

#9 Evan Davey wrote this 5 years, 4 months ago

To get it working today, I had to update all references to markdown.etree to markdown.util.etree.

Now seems ok with limited testing.

#10 Murph Murphy wrote this 5 years, 3 months ago

Just wanted to let you know I used this extension in a Django filter, which is open sourced. Is has a couple changes to make it work with Py2.7, and adds the filter of course. Anyone who needs a current working version can pull the mdx_video.py from github.com/skeet70/django-markdown-video. If you happen to need the full app feel free to snag that too ;)

Post a comment