1 # -*- coding: utf-8 -*-
3 """OPML importer and exporter (based on gPodder's "opml" module)
5 This module contains helper classes to import subscriptions from OPML files on
6 the web and to export a list of podcast objects to valid OPML 2.0 files.
11 import xml
.dom
.minidom
12 from xml
.parsers
.expat
import ExpatError
16 class Importer(object):
17 VALID_TYPES
= ("rss", "link")
19 def __init__(self
, content
):
21 Parses the OPML feed from the given URL into a local data structure
22 containing podcast metadata.
27 doc
= xml
.dom
.minidom
.parseString(content
)
28 except ExpatError
as e
:
29 raise ValueError from e
31 for outline
in doc
.getElementsByTagName("outline"):
33 outline
.getAttribute("type") in self
.VALID_TYPES
34 and outline
.getAttribute("xmlUrl")
35 or outline
.getAttribute("url")
38 "url": outline
.getAttribute("xmlUrl")
39 or outline
.getAttribute("url"),
40 "title": outline
.getAttribute("title")
41 or outline
.getAttribute("text")
42 or outline
.getAttribute("xmlUrl")
43 or outline
.getAttribute("url"),
44 "description": outline
.getAttribute("text")
45 or outline
.getAttribute("xmlUrl")
46 or outline
.getAttribute("url"),
49 if channel
["description"] == channel
["title"]:
50 channel
["description"] = channel
["url"]
52 for attr
in ("url", "title", "description"):
53 channel
[attr
] = channel
[attr
].strip()
55 self
.items
.append(channel
)
58 class Exporter(object):
60 Helper class to export a list of channel objects to a local file in OPML
61 2.0 format. See www.opml.org for the OPML specification.
64 def __init__(self
, title
="my.gpodder.org Subscriptions"):
66 self
.created
= email
.utils
.formatdate(localtime
=True)
68 def generate(self
, channels
):
70 Creates a XML document containing metadata for each channel object in
71 the "channels" parameter, which should be a list of channel objects.
73 Returns: An OPML document as string
75 doc
= xml
.dom
.minidom
.Document()
77 opml
= doc
.createElement("opml")
78 opml
.setAttribute("version", "2.0")
81 def create_node(name
, content
):
82 node
= doc
.createElement(name
)
83 node
.appendChild(doc
.createTextNode(content
))
86 head
= doc
.createElement("head")
87 head
.appendChild(create_node("title", self
.title
or ""))
88 head
.appendChild(create_node("dateCreated", self
.created
))
89 opml
.appendChild(head
)
91 def create_outline(channel
):
92 from mygpo
.subscriptions
.models
import SubscribedPodcast
93 from mygpo
.podcasts
.models
import PodcastGroup
95 outline
= doc
.createElement("outline")
97 if isinstance(channel
, SubscribedPodcast
):
98 title
= channel
.podcast
.title
99 outline
.setAttribute("xmlUrl", channel
.ref_url
)
100 outline
.setAttribute("description", channel
.podcast
.description
or "")
101 outline
.setAttribute("type", "rss")
102 outline
.setAttribute("htmlUrl", channel
.podcast
.link
or "")
103 elif isinstance(channel
, PodcastGroup
):
104 title
= channel
.title
105 for subchannel
in channel
.podcast_set
.all():
106 outline
.appendChild(create_outline(subchannel
))
108 title
= channel
.title
109 outline
.setAttribute("xmlUrl", channel
.url
)
110 outline
.setAttribute("description", channel
.description
or "")
111 outline
.setAttribute("type", "rss")
112 outline
.setAttribute("htmlUrl", channel
.link
or "")
114 outline
.setAttribute("title", title
or "")
115 outline
.setAttribute("text", title
or "")
118 body
= doc
.createElement("body")
119 for channel
in channels
:
120 body
.appendChild(create_outline(channel
))
121 opml
.appendChild(body
)
123 return doc
.toprettyxml(encoding
="utf-8", indent
=" ", newl
=os
.linesep
)