[API] handle Podcast groups in OPML
[mygpo.git] / mygpo / api / opml.py
blob2ee6d42292a5f804edb19d939cad7d64b364c77f
1 # -*- coding: utf-8 -*-
3 # This file is part of my.gpodder.org.
5 # my.gpodder.org is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Affero General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or (at your
8 # option) any later version.
10 # my.gpodder.org is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
13 # License for more details.
15 # You should have received a copy of the GNU Affero General Public License
16 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
19 """OPML importer and exporter (based on gPodder's "opml" module)
21 This module contains helper classes to import subscriptions from OPML files on
22 the web and to export a list of podcast objects to valid OPML 1.1 files.
23 """
25 import os
27 import xml.dom.minidom
28 import email.Utils
31 class Importer(object):
32 VALID_TYPES = ('rss', 'link')
34 def __init__(self, content):
35 """
36 Parses the OPML feed from the given URL into a local data structure
37 containing podcast metadata.
38 """
39 self.items = []
40 doc = xml.dom.minidom.parseString(content)
42 for outline in doc.getElementsByTagName('outline'):
43 if outline.getAttribute('type') in self.VALID_TYPES and \
44 outline.getAttribute('xmlUrl') or \
45 outline.getAttribute('url'):
46 channel = {
47 'url': outline.getAttribute('xmlUrl') or \
48 outline.getAttribute('url'),
49 'title': outline.getAttribute('title') or \
50 outline.getAttribute('text') or \
51 outline.getAttribute('xmlUrl') or \
52 outline.getAttribute('url'),
53 'description': outline.getAttribute('text') or \
54 outline.getAttribute('xmlUrl') or \
55 outline.getAttribute('url'),
58 if channel['description'] == channel['title']:
59 channel['description'] = channel['url']
61 for attr in ('url', 'title', 'description'):
62 channel[attr] = channel[attr].strip()
64 self.items.append(channel)
67 class Exporter(object):
68 """
69 Helper class to export a list of channel objects to a local file in OPML
70 1.1 format. See www.opml.org for the OPML specification.
71 """
73 def __init__(self, title='my.gpodder.org Subscriptions'):
74 self.title = title
75 self.created = email.Utils.formatdate(localtime=True)
77 def generate(self, channels):
78 """
79 Creates a XML document containing metadata for each channel object in
80 the "channels" parameter, which should be a list of channel objects.
82 Returns: An OPML document as string
83 """
84 doc = xml.dom.minidom.Document()
86 opml = doc.createElement('opml')
87 opml.setAttribute('version', '2.0')
88 doc.appendChild(opml)
90 def create_node(name, content):
91 node = doc.createElement(name)
92 node.appendChild(doc.createTextNode(content))
93 return node
95 head = doc.createElement('head')
96 head.appendChild(create_node('title', self.title or ''))
97 head.appendChild(create_node('dateCreated', self.created))
98 opml.appendChild(head)
100 def create_outline(channel):
101 from mygpo.subscriptions.models import SubscribedPodcast
102 from mygpo.podcasts.models import PodcastGroup
103 if isinstance(channel, SubscribedPodcast):
104 title = channel.podcast.title
105 description = channel.podcast.description
106 url = channel.ref_url
107 elif isinstance(channel, PodcastGroup):
108 title = channel.title
109 podcast = channel.podcast_set.first()
110 description = podcast.description
111 url = podcast.url
112 else:
113 title = channel.title
114 description = channel.description
115 url = channel.url
117 outline = doc.createElement('outline')
118 outline.setAttribute('title', title or '')
119 outline.setAttribute('text', description or '')
120 outline.setAttribute('xmlUrl', url)
121 outline.setAttribute('type', 'rss')
122 return outline
124 body = doc.createElement('body')
125 for channel in channels:
126 body.appendChild(create_outline(channel))
127 opml.appendChild(body)
129 return doc.toprettyxml(encoding='utf-8', \
130 indent=' ', \
131 newl=os.linesep)