@@ -40,7 +40,7 @@ class Episode(object):
40
40
ValueError if you set an attribute to an invalid value.
41
41
42
42
You must have filled in either :attr:`.title` or :attr:`.summary` before
43
- the RSS is generated.
43
+ the RSS can be generated.
44
44
45
45
To add an episode to a podcast::
46
46
@@ -51,12 +51,12 @@ class Episode(object):
51
51
52
52
You may also replace the last two lines with a shortcut::
53
53
54
- >>> episode = p.add_episode(Episode())
54
+ >>> episode = p.add_episode(podgen. Episode())
55
55
56
56
57
57
.. seealso::
58
58
59
- The :doc:`Basic Usage Guide < /user/basic_usage_guide/part_2> `
59
+ :doc:`/user/basic_usage_guide/part_2`
60
60
A friendlier introduction to episodes.
61
61
"""
62
62
@@ -79,19 +79,24 @@ def __init__(self, **kwargs):
79
79
up to 4000 characters in length.
80
80
81
81
See also :py:attr:`.Episode.subtitle` and
82
- :py:attr:`.Episode.long_summary`."""
82
+ :py:attr:`.Episode.long_summary`.
83
+
84
+ :type: :obj:`str` which can be parsed as XHTML.
85
+ :RSS: description"""
83
86
84
87
self .long_summary = None
85
88
"""A long (read: full) summary, which supplements the shorter
86
- :attr:`~podgen.Episode.summary`.
89
+ :attr:`~podgen.Episode.summary`. Like summary, this must be compatible
90
+ with XHTML parsers; use :func:`podgen.htmlencode` if this isn't HTML.
87
91
88
92
This attribute should be seen as a full, longer variation of
89
93
summary if summary exists. Even then, the long_summary should be
90
94
independent from summary, in that you only need to read one of them.
91
95
This means you may have to repeat the first sentences.
92
96
93
- If summary does not exist but this does, this is used in place of
94
- summary."""
97
+ :type: :obj:`str` which can be parsed as XHTML.
98
+ :RSS: content:encoded or description
99
+ """
95
100
96
101
self .__media = None
97
102
@@ -101,7 +106,7 @@ def __init__(self, **kwargs):
101
106
If not present, the URL of the enclosed media is used. This is usually
102
107
the best way to go, **as long as the media URL doesn't change**.
103
108
104
- Set the id to boolean False if you don't want to associate any id to
109
+ Set the id to boolean `` False`` if you don't want to associate any id to
105
110
this episode.
106
111
107
112
It is important that an episode keeps the same ID until the end of time,
@@ -116,17 +121,28 @@ def __init__(self, **kwargs):
116
121
domain which you own (for example, use something like
117
122
http://example.org/podcast/episode1 if you own example.org).
118
123
119
- This property corresponds to the RSS GUID element."""
124
+ :type: :obj:`str`, :obj:`None` to use default or :obj:`False` to leave
125
+ out.
126
+ :RSS: guid
127
+ """
120
128
121
129
self .link = None
122
- """The link to the full version of this episode description.
123
- Remember to start the link with the scheme, e.g. https://."""
130
+ """The link to the full version of this episode's :attr:`.summary`.
131
+ Remember to start the link with the scheme, e.g. https://.
132
+
133
+ :type: :obj:`str`
134
+ :RSS: link
135
+ """
124
136
125
137
self .__publication_date = None
126
138
127
139
self .title = None
128
140
"""This episode's human-readable title.
129
- Title is mandatory and should not be blank."""
141
+ Title is mandatory and should not be blank.
142
+
143
+ :type: :obj:`str`
144
+ :RSS: title
145
+ """
130
146
131
147
# ITunes tags
132
148
# http://www.apple.com/itunes/podcasts/specs.html#rss
@@ -138,20 +154,27 @@ def __init__(self, **kwargs):
138
154
139
155
self .__explicit = None
140
156
141
- self .is_closed_captioned = None
142
- """Whether this podcast includes a video episode with embedded closed
143
- captioning support.
157
+ self .is_closed_captioned = False
158
+ """Whether this podcast includes a video episode with embedded ` closed
159
+ captioning`_ support. Defaults to ``False`` .
144
160
145
- The two values for this tag are ``True`` and
146
- ``False``."""
161
+ :type: :obj:`bool`
162
+ :RSS: itunes:isClosedCaptioned
163
+
164
+ .. _closed captioning: https://en.wikipedia.org/wiki/Closed_captioning
165
+ """
147
166
148
167
self .__position = None
149
168
150
169
self .subtitle = None
151
170
"""A short subtitle.
152
171
153
172
This is shown in the Description column in iTunes.
154
- The subtitle displays best if it is only a few words long."""
173
+ The subtitle displays best if it is only a few words long.
174
+
175
+ :type: :obj:`str`
176
+ :RSS: itunes:subtitle
177
+ """
155
178
156
179
# It is time to assign the keyword arguments
157
180
for attribute , value in iteritems (kwargs ):
@@ -162,7 +185,13 @@ def __init__(self, **kwargs):
162
185
"recognized!" % (attribute , value ))
163
186
164
187
def rss_entry (self ):
165
- """Create a RSS item and return it."""
188
+ """Create an RSS item using lxml's etree and return it.
189
+
190
+ This is primarily used by :class:`podgen.Podcast` when generating the
191
+ podcast's RSS feed.
192
+
193
+ :returns: etree.Element('item')
194
+ """
166
195
167
196
ITUNES_NS = 'http://www.itunes.com/dtds/podcast-1.0.dtd'
168
197
DUBLIN_NS = 'http://purl.org/dc/elements/1.1/'
@@ -279,8 +308,8 @@ def authors(self):
279
308
"""List of :class:`~podgen.Person` that contributed to this
280
309
episode.
281
310
282
- The authors don't need to have both name and email set. The names are
283
- shown under the podcast's title on iTunes .
311
+ The authors don't need to have both name and email set. They're usually
312
+ not displayed anywhere .
284
313
285
314
.. note::
286
315
@@ -308,6 +337,9 @@ def authors(self):
308
337
>>> # Or assign a new list (discarding earlier authors)
309
338
>>> ep.authors = [Person("John Doe", "[email protected] "),
310
339
... Person("Mary Sue", "[email protected] ")]
340
+
341
+ :type: :obj:`list` of :class:`podgen.Person`
342
+ :RSS: author or dc:creator, and itunes:author
311
343
"""
312
344
return self .__authors
313
345
@@ -322,11 +354,22 @@ def authors(self, authors):
322
354
323
355
@property
324
356
def publication_date (self ):
325
- """Set or get the time that this episode first was made public.
357
+ """The time and date this episode was first published.
358
+
359
+ The value can be a :obj:`str`, which will be parsed and
360
+ made into a :class:`datetime.datetime` object when assigned. You may
361
+ also assign a :class:`datetime.datetime` object directly. In both cases,
362
+ you must ensure that the value includes timezone information.
363
+
364
+ :type: :obj:`str` (will be converted to and stored as
365
+ :class:`datetime.datetime`) or :class:`datetime.datetime`.
366
+ :RSS: pubDate
367
+
368
+ .. note::
326
369
327
- The value can either be a string which will automatically be parsed or a
328
- datetime.datetime object. In both cases you must ensure that the value
329
- includes timezone information.
370
+ Don't use the media file's modification date as the publication
371
+ date, unless they're the same. It looks very odd when an episode
372
+ suddenly pops up in the feed, but it claims to be several hours old!
330
373
"""
331
374
return self .__publication_date
332
375
@@ -348,13 +391,16 @@ def media(self):
348
391
"""Get or set the :class:`~podgen.Media` object that is attached
349
392
to this episode.
350
393
351
- Note that if :py:attr:`.id` is not set, the enclosure's url is used as
352
- the globally unique identifier. If you rely on this, you should make
353
- sure the url never changes, since changing the id messes up with clients
354
- (they will think this episode is new again, even if the user already
355
- has listened to it). Therefore, you should only rely on this behaviour
356
- if you own the domain which the episodes reside on. If you don't, then
357
- you must set :py:attr:`.id` to an appropriate value manually.
394
+ Note that if :py:attr:`.id` is not set, the media's URL is used as
395
+ the id. If you rely on this, you should make sure the URL never changes,
396
+ since changing the id messes up with clients (they will think this
397
+ episode is new again, even if the user has listened to it already).
398
+ Therefore, you should only rely on this behaviour if you own the domain
399
+ which the episodes reside on. If you don't, then you must set
400
+ :py:attr:`.id` to an appropriate value manually.
401
+
402
+ :type: :class:`podgen.Media`
403
+ :RSS: enclosure and itunes:duration
358
404
"""
359
405
return self .__media
360
406
@@ -374,17 +420,20 @@ def media(self, media):
374
420
375
421
@property
376
422
def withhold_from_itunes (self ):
377
- """Get or set the iTunes block attribute. Use this to prevent episodes
378
- from appearing in the iTunes podcast directory. Note that the episode
379
- can still be found by inspecting the XML, so it is still public.
423
+ """Prevent this episode from appearing in the iTunes podcast directory.
424
+ Note that the episode can still be found by inspecting the XML, so it is
425
+ still public.
380
426
381
- One use case is if you know that this episode will get you kicked
427
+ One use case would be if you knew that this episode would get you kicked
382
428
out from iTunes, should it make it there. In such cases, you can set
383
429
withhold_from_itunes to ``True`` so this episode isn't published on
384
430
iTunes, allowing you to publish it to everyone else while keeping your
385
431
podcast on iTunes.
386
432
387
433
This attribute defaults to ``False``, of course.
434
+
435
+ :type: :obj:`bool`
436
+ :RSS: itunes:block
388
437
"""
389
438
return self .__withhold_from_itunes
390
439
@@ -401,29 +450,35 @@ def withhold_from_itunes(self, withhold_from_itunes):
401
450
402
451
@property
403
452
def image (self ):
404
- """The podcast episode's image.
453
+ """The podcast episode's image, overriding the podcast's
454
+ :attr:`~.Podcast.image`.
455
+
456
+ This attribute specifies the absolute URL to the artwork for your
457
+ podcast. iTunes prefers square images that are at least ``1400x1400``
458
+ pixels.
459
+
460
+ iTunes supports images in JPEG and PNG formats with an RGB color space
461
+ (CMYK is not supported). The URL must end in ".jpg" or ".png".
462
+
463
+ :type: :obj:`str`
464
+ :RSS: itunes:image
465
+
466
+ .. note::
467
+
468
+ If you change an episode’s image, you should also change the file’s
469
+ name; iTunes doesn't check the actual file to see if it's changed.
470
+
471
+ Additionally, the server hosting your cover art image must allow HTTP
472
+ HEAD requests.
405
473
406
474
.. warning::
407
475
408
476
Almost no podcatchers support this. iTunes supports it only if you
409
477
embed the cover in the media file (the same way you would embed
410
- an album cover), and recommends you use Garageband's Enhanced
411
- Podcast feature. If you don't, the podcast's image is used instead.
478
+ an album cover), and recommends that you use Garageband's Enhanced
479
+ Podcast feature.
412
480
413
- This tag specifies the artwork for your podcast.
414
- iTunes prefers square .jpg images that are at least 1400x1400 pixels,
415
- which is different from what is specified for the standard RSS image
416
- tag. In order for a podcast to be eligible for an iTunes Store feature,
417
- the accompanying image must be at least 1400x1400 pixels.
418
-
419
- iTunes supports images in JPEG and PNG formats with an RGB color space
420
- (CMYK is not supported). The URL must end in ".jpg" or ".png".
421
-
422
- If you change an episode’s image, you should also change the file’s
423
- name. iTunes may not change the image if it checks your feed and the
424
- image URL is the same. The server hosting your cover art image must
425
- allow HTTP head requests for iTunes to be able to automatically update
426
- your cover art.
481
+ The podcast's image is used if this isn't supported.
427
482
"""
428
483
return self .__image
429
484
@@ -444,13 +499,16 @@ def explicit(self):
444
499
inappropriate for children.
445
500
446
501
The value of the podcast's explicit attribute is used by default, if
447
- this is ``None``.
502
+ this is kept as ``None``.
448
503
449
504
If you set this to ``True``, an "explicit" parental advisory
450
505
graphic will appear in the Name column in iTunes. If the value is
451
506
``False``, the parental advisory type is considered Clean, meaning that
452
507
no explicit language or adult content is included anywhere in this
453
508
episode, and a "clean" graphic will appear.
509
+
510
+ :type: :obj:`bool`
511
+ :RSS: itunes:explicit
454
512
"""
455
513
return self .__explicit
456
514
@@ -471,9 +529,14 @@ def position(self):
471
529
472
530
If you would like this episode to appear first, set it to ``1``.
473
531
If you want it second, set it to ``2``, and so on. If multiple episodes
474
- share the same position, they will be sorted by their publication date.
532
+ share the same position, they will be sorted by their
533
+ :attr:`publication date <.Episode.publication_date>`.
534
+
535
+ To remove the order from the episode, set the position back to
536
+ :obj:`None`.
475
537
476
- To remove the order from the episode, set the position back to ``None``.
538
+ :type: :obj:`int`
539
+ :RSS: itunes:order
477
540
"""
478
541
return self .__position
479
542
0 commit comments