Repository: getnikola/nikola

Issue:
Support Enclosures
It would be awfully convenient if Nikola supported enclosures.  To quote IRC:

```
<ralsina_> [Tritium]: yes, we have no enclosure support. Should be easy to add, tho!
<ralsina_> [Tritium]: mostly, you'd have to add the paths in metadata in the post, and then change the RSS generation to look for it and add enclosures as needed
<ralsina_> [Tritium]: maybe 10 lines of code, really
```

Sadly, I do not know the code base well enough to write a sane patch myself.



Discussion:
This is supported. Kind of.

https://github.com/getnikola/nikola/commit/1d31d31a7d2db0fa332260465ba7275463bb03e9

Example use,
https://github.com/getnikola/plugins/blob/master/v7/speechsynthesizednetcast/speechsynthesizednetcast.py#L173
https://github.com/getnikola/plugins/blob/master/v7/speechsynthesizednetcast/speechsynthesizednetcast.py#L193

I saw that in the Nikola codebase, but didnt know about the speech synthases module.  I think, however, after digging through the code, the most ideal way to do what I want is to patch nikola.nikola.Nikola.  Such a patch would add an optional `enclosure` metadata tag, and add it to the default RSS.  Should be about a ten line patch.


Resolving patch:
diff --git a/nikola/nikola.py b/nikola/nikola.py
--- a/nikola/nikola.py
+++ b/nikola/nikola.py
@@ -34,6 +34,7 @@
 import locale
 import os
 import sys
+import mimetypes
 try:
     from urlparse import urlparse, urlsplit, urljoin
 except ImportError:
@@ -163,6 +164,16 @@
 }
 
 
+def _enclosure(post, lang):
+    '''Default implementation of enclosures'''
+    enclosure = post.meta('enclosure', lang)
+    if enclosure:
+        length = 0
+        url = enclosure
+        mime = mimetypes.guess_type(url)[0]
+        return url, length, mime
+
+
 class Nikola(object):
 
     """Class that handles site generation.
@@ -875,7 +886,7 @@ def url_replacer(self, src, dst, lang=None):
         return result
 
     def generic_rss_renderer(self, lang, title, link, description, timeline, output_path,
-                             rss_teasers, rss_plain, feed_length=10, feed_url=None, enclosure=None):
+                             rss_teasers, rss_plain, feed_length=10, feed_url=None, enclosure=_enclosure):
 
         """Takes all necessary data, and renders a RSS feed in output_path."""
         rss_obj = rss.RSS2(
@@ -929,14 +940,11 @@ def generic_rss_renderer(self, lang, title, link, description, timeline, output_
                 rss_obj.rss_attrs["xmlns:dc"] = "http://purl.org/dc/elements/1.1/"
 
             """ Enclosure callback must returns tuple """
-            if enclosure:
-                download_link, download_size, download_type = enclosure(post=post, lang=lang)
-
-                args['enclosure'] = rss.Enclosure(
-                    download_link,
-                    download_size,
-                    download_type,
-                )
+            # enclosure callback returns None if post has no enclosure, or a
+            # 3-tuple of (url, length (0 is valid), mimetype)
+            enclosure_details = enclosure(post=post, lang=lang)
+            if enclosure_details is not None:
+                args['enclosure'] = rss.Enclosure(*enclosure_details)
 
             items.append(utils.ExtendedItem(**args))
 


Test patch:
diff --git a/tests/test_rss_feeds.py b/tests/test_rss_feeds.py
--- a/tests/test_rss_feeds.py
+++ b/tests/test_rss_feeds.py
@@ -44,13 +44,16 @@ def setUp(self):
         self.blog_url = "http://some.blog"
 
         with mock.patch('nikola.post.get_meta',
-                        mock.Mock(return_value=({'title': 'post title',
-                                                 'slug': 'awesome_article',
-                                                 'date': '2012-10-01 22:41',
-                                                 'author': None,
-                                                 'tags': 'tags', 'link':
-                                                 'link', 'description':
-                                                 'description'}))):
+                        mock.Mock(return_value=(
+                            {'title': 'post title',
+                             'slug': 'awesome_article',
+                             'date': '2012-10-01 22:41',
+                             'author': None,
+                             'tags': 'tags',
+                             'link': 'link',
+                             'description': 'description',
+                             'enclosure': 'http://www.example.org/foo.mp3'}
+                        ))):
             with mock.patch('nikola.nikola.utils.os.path.isdir',
                             mock.Mock(return_value=True)):
                 with mock.patch('nikola.nikola.Post.text',