
    i߰                        d Z ddlmZmZmZmZ ddl ddlmZ ddl	m
Z
 ddlmZ ddlZddlZddlmZ dd	lmZ dd
lmZmZmZmZ ddlmZ ddlZddlZddlmZ ddlZddl Z ddl!Z!ejD                  jF                  Z$ G d de%      Z&y)z
    podgen.feed
    ~~~~~~~~~~~~

    :copyright: 2013, Lars Kiesow <lkiesow@uos.de> and 2016, Thorben Dahl
        <thorben@sjostrom.no>

    :license: FreeBSD and LGPL, see license.* for more details.

    )absolute_importdivisionprint_functionunicode_literals)*)	iteritems)etree)datetimeN)Episode)NotSupportedByItunesWarning)ensure_formatformatRFC2822listToHumanreadableStr
htmlencode)Person)string_typesc                   V   e Zd ZdZd Zed        Zej                  d        Zed        Zej                  d        Zd,dZ	d	 Z
d
 Zd Zd Z	 	 d-dZ	 	 d-dZd Zd Zed        Zej                  d        Zed        Zej                  d        Z	 	 d.dZd/dZed        Zed        Zej                  d        Zed        Zej                  d        Zed        Zej                  d        Zed        Zej                  d        Zed         Zej                  d!        Zed"        Zej                  d#        Zed$        Zej                  d%        Zed&        Zej                  d'        Zed(        Zej                  d)        Zed*        Z e j                  d+        Z y)0Podcasta  Class representing one podcast feed.

    The following attributes are mandatory:

    * :attr:`~podgen.Podcast.name`
    * :attr:`~podgen.Podcast.website`
    * :attr:`~podgen.Podcast.description`
    * :attr:`~podgen.Podcast.explicit`

    All attributes can be assigned :obj:`None` in addition to the types
    specified below. Types etc. are checked during assignment, to help you
    discover errors earlier. Duck typing is employed wherever a class in podgen
    is expected.

    There is a **shortcut** you can use when creating new Podcast objects, that
    lets you populate the attributes using the constructor. Use keyword
    arguments with the **attribute name as keyword** and the desired value as
    value. As an example::

        >>> import podgen
        >>> # The following...
        >>> p = Podcast()
        >>> p.name = "The Test Podcast"
        >>> p.website = "http://example.com"
        >>> # ...is the same as this:
        >>> p = Podcast(
        ...     name="The Test Podcast",
        ...     website="http://example.com",
        ... )

    Of course, you can do this for as many (or few) attributes as you like, and
    you can still set the attributes afterwards, like always.

    :raises: TypeError if you use a keyword which isn't recognized as an
        attribute. ValueError if you use a value which isn't compatible with
        the attribute (just like when you assign it manually).

    c                 L   g | _         	 t        | _        	 ddddd| _        	 d | _        	 d | _        	 d | _        	 d | _        	 d | _        d | _	        	 d| _
        | j                  | _        	 d | _        	 d | _        g | _        d | _        d | _        d | _        d | _        d | _        d| _        	 d | _        d | _        d | _        d | _        	 d | _        d | _        	 d | _        	 d | _        	 t=        |      D ]0  \  }}t?        | |      rtA        | ||        tC        d|d	|d
       y )Nzhttp://www.w3.org/2005/Atomz(http://purl.org/rss/1.0/modules/content/z*http://www.itunes.com/dtds/podcast-1.0.dtdz http://purl.org/dc/elements/1.1/)atomcontentitunesdcz)http://www.rssboard.org/rss-specificationFzKeyword argument z (with value z)) doesn't match any attribute in Podcast.)"_Podcast__episodesr   _Podcast__episode_class_nsmapnamewebsitedescriptionexplicit_Podcast__cloud	copyright_Podcast__docs_feedgen_generator_str	generatorlanguage_Podcast__last_updated_Podcast__authors_Podcast__publication_date_Podcast__skip_hours_Podcast__skip_days_Podcast__web_master_Podcast__feed_urlwithhold_from_itunes_Podcast__category_Podcast__image_Podcast__completenew_feed_url_Podcast__ownersubtitlepubsubhubbubxsltr   hasattrsetattr	TypeError)selfkwargs	attributevalues       G/root/podcast_feed/.venv/lib/python3.12/site-packages/podgen/podcast.py__init__zPodcast.__init__M   s   -&6 3AB4	
	 		 	  	 	$ 	 B44	 	 #"&   %*!	$  	0 	 !	0 		. !*& 1IutY'i/!*E!3 4 4	 !2    c                     | j                   S )a  List of :class:`.Episode` objects that are part of this podcast.

        See :py:meth:`.add_episode` for an easy way to create new episodes and
        assign them to this podcast in one call.

        :type: :obj:`list` of :class:`podgen.Episode`
        :RSS: item elements
        )r   r:   s    r>   episodeszPodcast.episodesF  s     r@   c                 T    t        |t              st        |      | _        y || _        y N)
isinstancelistr   )r:   rC   s     r>   rC   zPodcast.episodesR  s"     1;8T0J$x. 	r@   c                     | j                   S )a\  Class used to represent episodes.

        This is used by :py:meth:`.add_episode` when creating new episode
        objects, and you, too, may use it when creating episodes.

        By default, this property points to :py:class:`.Episode`.

        When assigning a new class to ``episode_class``, you must make sure that
        the new value (1) is a class and not an instance, and (2) that it is a
        subclass of Episode (or is Episode itself).

        Example of use::

            >>> # Create new podcast
            >>> from podgen import Podcast, Episode
            >>> p = Podcast()

            >>> # Normal way of creating new episodes
            >>> episode1 = Episode()
            >>> p.episodes.append(episode1)

            >>> # Or use add_episode (and thus episode_class indirectly)
            >>> episode2 = p.add_episode()

            >>> # Or use episode_class directly
            >>> episode3 = p.episode_class()
            >>> p.episodes.append(episode3)

            >>> # Say you want to use AlternateEpisode class instead of Episode
            >>> from mymodule import AlternateEpisode
            >>> p.episode_class = AlternateEpisode

            >>> episode4 = p.add_episode()
            >>> episode4.title("This is an instance of AlternateEpisode!")

        :type: :obj:`class` which extends :class:`podgen.Episode`
        )r   rB   s    r>   episode_classzPodcast.episode_classX  s    N ###r@   c                     t        j                  |      st        d      t        |t              r|| _        y t        d      )NzNew episode_class must NOT be an _instance_ of the desired class, but rather the class itself. You can generally achieve this by removing the parenthesis from the constructor call. For example, use Episode, not Episode().zQNew episode_class must be Episode or a descendant of it (so the API still works).)inspectisclass
ValueError
issubclassr   r   )r:   r=   s     r>   rI   zPodcast.episode_class  sL    u% D E E
 w'#(D  @ A Ar@   Nc                 `    || j                         }| j                  j                  |       |S )a  Shorthand method which adds a new episode to the feed, creating an
        object if it's not provided, and returns it. This
        is the easiest way to add episodes to a podcast.

        :param new_episode: :class:`.Episode` object to add. A new instance of
            :attr:`.episode_class` is used if ``new_episode`` is omitted.
        :returns: Episode object created or passed to this function.

        Example::

            ...
            >>> episode1 = p.add_episode()
            >>> episode1.title = 'First episode'
            >>> # You may also provide an episode object yourself:
            >>> another_episode = p.add_episode(podgen.Episode())
            >>> another_episode.title = 'My second episode'

        Internally, this method creates a new instance of
        :attr:`~podgen.Episode.episode_class`, which means you can change what
        type of objects are created by changing
        :attr:`~podgen.Episode.episode_class`.

        )rI   rC   append)r:   new_episodes     r>   add_episodezPodcast.add_episode  s0    0 ,,.K[)r@   c                    | j                   d   }t        j                  dd| j                         }t        j                  |d      }| j                  r$| j
                  r| j                  r| j                  ddj                  | j                  rg ndg| j
                  rg nd	gz   | j                  rg nd
gz   | j                  g ndgz         }t        d|z        t        j                  |d      }| j                  |_
        t        j                  |d      }| j
                  |_
        t        j                  |d
      }| j                  |_
        t        j                  |d|z        }| j                  rdnd|_
        | j                  rt        j                  |d      }	| j                  j                  d      |	j                  d<   t        | j                  j                  d            |	j                  d<   | j                  j                  d      |	j                  d<   | j                  j                  d      |	j                  d<   | j                  j                  d      |	j                  d<   | j                  r't        j                  |d      }
| j                  |
_
        | j                   r't        j                  |d      }| j                   |_
        | j"                  r't        j                  |d      }| j"                  |_
        | j$                  r't        j                  |d      }| j$                  |_
        | j&                  2t)        j*                  t,        j.                  j1                               }n| j&                  }|r&t        j                  |d      }t3        |      |_
        | j4                  rd| j4                  D cg c]  }|j                  s|j                   }}|r)t        j                  |d|z        }t7        |      |_
        t9        | j4                        dkD  s| j4                  d   j:                  s| j4                  xs g D ]  }t        j                  |d | j                   d!   z        }|j                  r.|j:                  r"|j                  d"|j:                  d#|_
        c|j                  r|j                  |_
        |j:                  |_
         n3t        j                  |d$      }t        | j4                  d         |_
        | j<                  @| j>                  D cg c]  }|j<                  |j<                   }}|rtA        |      }nd}n| j<                  }|r&t        j                  |d%      }t3        |      |_
        | jB                  r^| jB                  | _!        t        j                  |d&      }| jB                  D ](  }t        j                  |d'      }t        |      |_
        * | jD                  rU| jD                  | _"        t        j                  |d(      }| jD                  D ]  }t        j                  |d)      }||_
        ! | jF                  rQ| jF                  j:                  stI        d*      t        j                  |d+      }t        | jF                        |_
        | jJ                  r t        j                  |d,|z        }d-|_
        | jL                  rt        j                  |d.|z        } | jL                  jL                  | j                  d/<   | jL                  jN                  r<t        j                  | d.|z        }!| jL                  jN                  |!j                  d/<   | jP                  r2t        j                  |d0|z        }"| jP                  |"j                  d1<   | jR                  r t        j                  |d2|z        }#d-|#_
        | jT                  r*t        j                  |d3|z        }$| jT                  |$_
        | jV                  rt        j                  |d4|z        }%t        j                  |%d5|z        }&| jV                  j                  |&_
        t        j                  |%d6|z        }'| jV                  j:                  |'_
        | jX                  r*t        j                  |d7|z        }(| jX                  |(_
        | jZ                  r]t        j                  |d8| j                   d9   z        })| jZ                  |)j                  d1<   d:|)j                  d;<   d<|)j                  d=<   | j\                  rNt        j                  |d8| j                   d9   z        }*| j\                  |*j                  d1<   d>|*j                  d;<   | j>                  D ]#  }+|+j_                         },|ja                  |,       % |S c c}w c c}w )?zCreate an RSS feed XML structure containing all previously set fields.

        :returns: The root element (ie. the rss element) of the feed.
        :rtype: lxml.etree.Element
        r   rssz2.0)versionnsmapchannelNz, r   r   r   r    zRequired fields not set (%s)titlelinkz{%s}explicityesnoclouddomainportpathregisterProcedureprotocolr"   docsr%   r&   lastBuildDatez
{%s}author   r   z{%s}creatorr   z <>managingEditorpubDate	skipHourshourskipDaysdayzawebMaster must have an email. Did you set email to None after assigning that Person to webMaster?	webMasterz	{%s}blockYesz{%s}categorytextz	{%s}imagehrefz{%s}completez{%s}new-feed-urlz	{%s}ownerz{%s}namez	{%s}emailz{%s}subtitlez{%s}linkr   r:   relzapplication/rss+xmltypehub)1r   r	   Element
SubElementr   r   r   r    joinrM   rn   r!   getattribstrr"   r#   r%   r&   last_updatedr
   nowdateutiltztzutcr   authorsr   lenemailpublication_daterC   max
skip_hours	skip_days
web_masterRuntimeErrorr.   categorysubcategoryimagecompleter2   ownerr4   feed_urlr5   	rss_entryrP   )-r:   	ITUNES_NSfeedrW   missingrX   rY   descr    r\   r"   rb   r%   r&   lastBuildDateDaterc   aauthors_with_nameitunes_authorauthoreepisode_datesactual_pubDaterg   rh   hri   rj   drk   rl   blockr   r   r   r   r2   r   
owner_nameowner_emailr4   link_to_selflink_to_hubentryitems-                                                r>   _create_rsszPodcast._create_rss  s    KK)	}}UEE""43		dllt/?/?MM-iityyvh'+||)!F'+'7'7m_!N (,}}'@zl!T UG ;gEFF  '2YY
0LL	7$$	##G^i-GH!%D<<$$Wg6E%)\\%5%5h%?ELL"#&t||'7'7'?#@ELL #'<<#3#3F#;ELL 040@0@'1)ELL,-'+||'7'7
'CELL$>>((+>I!^^IN;;##GV4DDI>>((+>I!^^IN==''<H MMHM$ (X[[->->-@ A $ 1 1!,,WoFM!./@!AM<<15 HA H  $$WlY.FG %;<M%N"4<< 1$DLLO,A,A ++A"--g.;dkk$>O.OQFvv!''3466177&C&'ff&'gg , ))'3CD!$,,q/2  (9= @A ! 2 2 > //M @!$]!3!%!22N&&w	:G(8GL??"ooDO((+>I__''	6:F	 % >>!^^DN''<H^^&&x7 $ ????((" $: ; ; ((+>I 1IN$$$$WkI.EFEEJ=='')1KLH&*mm&<&<HOOF#}}((#..x)9ST-1]]-F-F""6*::$$WkI.EFE#'::ELL =='')1KLH!HM ++G5G)5STL $ 1 1L::$$WkI.EFE))%i1GHJ"jjooJO**5+	2IJK#zz//K=='')1KLH MMHM== ++GZ$++fBU5UVL*.--L')/L&*?L'**7JVAT4TUK)-):):Kv&(-Ku%]]E??$DNN4  # Y !I2@s   g)g);g.c                 b    | j                         }|r|j                  dd|z  d      S |dz   |z   S )z=Add an XSLT processor instruction to the RSS string provided.
z
%s
rd   )_get_xslt_pireplace)r:   rT   xml_declarationpis       r>   _add_xslt_pizPodcast._add_xslt_piR  sC      ;;]  9s?"r@   c                     t        | j                        }|j                  dd      j                  dd      }t        j                  t        j
                  dd|z   dz         d      j                  d      S )N" \zxml-stylesheetztype="text/xsl" href="UTF-8encoding)r   r6   r   r	   tostringProcessingInstructiondecode)r:   htmlescaped_urlquote_sanitizeds      r>   r   zPodcast._get_xslt_pif  sm    $TYY/)11#r:BB4L~~e99$6<
  $VG_	-r@   c                 "    | j                         S )zPrint the podcast in RSS format, using the default options.

        This method just calls :py:meth:`.rss_str` without arguments.
        )rss_strrB   s    r>   __str__zPodcast.__str__n  s    
 ||~r@   c                     | j                         }t        j                  || ||      j                  |      }| j                  r| j                  ||      S |S )ax  Generate an RSS feed and return the feed XML as string.

        :param minimize: Set to True to disable splitting the feed into multiple
            lines and adding properly indentation, saving bytes at the cost of
            readability (default: False).
        :type minimize: bool
        :param encoding: Encoding used in the XML declaration (default: UTF-8).
        :type encoding: str
        :param xml_declaration: Whether an XML declaration should be added to
            the output (default: True).
        :type xml_declaration: bool
        :returns: The generated RSS feed as a :obj:`str` (unicode in 2.7)
        )pretty_printr   r   )r   )r   r	   r   r   r6   r   )r:   minimizer   r   r   rT   s         r>   r   zPodcast.rss_stru  sZ     !nnTHx.=??Evh?O 	99$$S/$JJJr@   c                    | j                  |||      }t        |t              r)t        |d|      5 }|j	                  |       ddd       yt        |d      r|j	                  |       yt        d|z        # 1 sw Y   yxY w)ag  Generate an RSS feed and write the resulting XML to a file.

        .. note::

           If atomicity is needed, then you are expected to provide that
           yourself. That means that you should write the feed to a temporary
           file which you rename to the final name afterwards; renaming is an
           atomic operation on Unix(like) systems.

        .. note::

           File-like objects given to this method will not be closed.

        :param filename: Name of file to write, or a file-like object (accepting
            string/unicode, not bytes).
        :type filename: str or fd
        :param minimize: Set to True to disable splitting the feed into multiple
            lines and adding properly indentation, saving bytes at the cost of
            readability (default: False).
        :type minimize: bool
        :param encoding: Encoding used in the XML file (default: UTF-8).
        :type encoding: str
        :param xml_declaration: Whether an XML declaration should be added to
            the output (default: True).
        :type xml_declaration: bool
        :returns: Nothing.
        )r   r   r   wr   Nwritezfilename must either be a filename (str/unicode) or a file-like object (with write method); %s satisfies none of those conditions.)r   rF   r   openr   r7   r9   )r:   filenamer   r   r   rT   fds          r>   rss_filezPodcast.rss_file  s    : llHx+:  < h-hh72 87Xw'NN3 EGOP Q Q 87s   A::Bc                 V    t        | j                        D ]  \  }}|dz   }||_         y)aL  Make sure that the episodes appear on iTunes in the exact order
        they have in :attr:`~.Podcast.episodes`.

        This will set each :attr:`.Episode.position` so it matches the episode's
        position in :attr:`.Podcast.episodes`.

        If you're using some :class:`.Episode` objects in multiple podcast
        feeds and you don't use this method with every feed, you might want to
        call :meth:`.Podcast.clear_episode_order` after generating this feed's
        RSS so an episode's position in this feed won't affect its position in
        the other feeds.
        rd   N)	enumeraterC   position)r:   iepisoder   s       r>   apply_episode_orderzPodcast.apply_episode_order  s,     $DMM2JAw1uH'G 3r@   c                 4    | j                   D ]	  }d|_         y)a^  Reset :attr:`.Episode.position` for every single episode.

        Use this if you want to reuse an :class:`.Episode` object in another
        feed, and don't want its position in this feed to affect where it
        appears in the other feed. This is not needed if you'll call
        :meth:`.Podcast.apply_episode_order` on the other feed, though.N)rC   r   )r:   r   s     r>   clear_episode_orderzPodcast.clear_episode_order  s     }}G#G %r@   c                     | j                   S )a  The last time the feed was generated. It defaults to the time and
        date at which the RSS is generated, if set to :obj:`None`. The default
        should be sufficient for most, if not all, use cases.

        The value can either be a string, which will automatically be parsed
        into a :class:`datetime.datetime` object when assigned, or a
        :class:`datetime.datetime` object. In any case, the time and date must
        be timezone aware.

        Set this to ``False`` to leave out this element instead of using the
        default.

        :type: :class:`datetime.datetime`, :obj:`str` (will be converted to
           and stored as :class:`datetime.datetime`), :obj:`None` for default or
           :obj:`False` to leave out.
        :RSS: lastBuildDate
        )r'   rB   s    r>   ry   zPodcast.last_updated  s    & """r@   c                     ||du r|| _         y t        |t              rt        j                  j                  |      }t        |t              st        d      |j                  t        d      || _         y NFzInvalid datetime formatz$Datetime object has no timezone info)	r'   rF   r   r{   parserparser
   rM   tzinfo)r:   ry   s     r>   ry   zPodcast.last_updated  sk    <5#8".D,5'44\BlH5 !:;;""* !GHH".Dr@   c                 d     g d} j                   rt         fd|D              S  j                   S )a  The cloud data of the feed, as a 5-tuple. It specifies a web service
        that supports the (somewhat dated) rssCloud interface, which can be
        implemented in HTTP-POST, XML-RPC or SOAP 1.1.

        The tuple should look like this: ``(domain, port, path,
        registerProcedure, protocol)``.

        :domain: The domain where the webservice can be found.
        :port: The port the webservice listens to.
        :path: The path of the webservice.
        :registerProcedure: The procedure to call.
        :protocol: Can be either "HTTP-POST", "xml-rpc" or "soap".

        Example::

            p.cloud = ("podcast.example.org", 80, "/rpc", "cloud.notify",
                       "xml-rpc")

        :type: :obj:`tuple` with (:obj:`str`, :obj:`int`, :obj:`str`,
           :obj:`str`, :obj:`str`)
        :RSS: cloud

        .. tip::

            PubSubHubbub is a competitor to rssCloud, and is the preferred
            choice if you're looking to set up a new service of this kind.
        r]   r^   r_   r`   ra   c              3   <   K   | ]  }j                   |     y wrE   )r!   ).0keyr:   s     r>   	<genexpr>z Podcast.cloud.<locals>.<genexpr>  s     =*3T\\#&*s   )r!   tuple)r:   
tuple_keyss   ` r>   r\   zPodcast.cloud  s0    : Q
AEu=*== 		r@   c                     |/	 |\  }}}}}|r|dk7  r|r|r|st        d      |||||d| _        y d | _        y # t         $ r t        d      w xY w)Nz0Value of cloud must either be None or a 5-tuple.Fz6All parameters of cloud must be present and not empty.r   )rM   r9   r!   )r:   r\   r]   r^   r_   r`   ra   s          r>   r\   zPodcast.cloud  s    ,BG?d$5x 4<M   "/ 0 0%+D(9hPDL  DL  , !+ , ,,s	   : Ac                 l    | j                  |||      |sd| j                  z  z   | _        ydz   | _        y)a
  Set the generator of the feed, formatted nicely, which identifies the
        software used to generate the feed.

        :param generator: Software used to create the feed.
        :type generator: str
        :param version: (Optional) Version of the software, as a tuple.
        :type version: :obj:`tuple` of :obj:`int`
        :param uri: (Optional) The software's website.
        :type uri: str
        :param exclude_podgen: (Optional) Set to True if you don't want
            PodGen to be mentioned (e.g., "My Program (using PodGen 1.0.0)")
        :type exclude_podgen: bool

        .. seealso::

           The attribute :py:attr:`.generator`
              Lets you access and set the generator string yourself, without
              any formatting help.
        z (using %s)r   N)_program_name_to_strr$   r%   )r:   r%   rU   uriexclude_podgens        r>   set_generatorzPodcast.set_generator'  s>    * 229gsK'5 ($*E*EE?;=?r@   c           
          ||+ddj                  |D cg c]  }t        |       c}      z   ndz   |rd|z   z   S dz   S c c}w )Nz v.r    )ru   rx   )r:   r%   rU   r   r   s        r>   r   zPodcast._program_name_to_str@  sZ    AHAT$7";7a3q67";<<Z\^ ##)- 	-)+- 	-";s   ?c                     | j                  t        j                  j                  t        j                  j                  t        j                  j
                        S rE   )r   podgenrU   r   version_fullr   rB   s    r>   r$   zPodcast._feedgen_generator_strE  s<    (('-~~':':'-~~'B'B'-~~'='=% 	%r@   c                     | j                   S )a  List of :class:`~podgen.Person` that are responsible for this
        podcast's editorial content.

        Any value you assign to authors will be automatically converted to a
        list, but only if it's iterable (like tuple, set and so on). It is an
        error to assign a single :class:`~podgen.person.Person` object to this
        attribute::

            >>> # This results in an error
            >>> p.authors = Person("John Doe", "johndoe@example.org")
            TypeError: Only iterable types can be assigned to authors, ...
            >>> # This is the correct way:
            >>> p.authors = [Person("John Doe", "johndoe@example.org")]

        The authors don't need to have both name and email set. The names are
        shown under the podcast's title on iTunes.

        The initial value is an empty list, so you can use the list methods
        right away.

        Example::

            >>> # This attribute is just a list - you can for example append:
            >>> p.authors.append(Person("John Doe", "johndoe@example.org"))
            >>> # Or they can be given as new list (overriding earlier authors)
            >>> p.authors = [Person("John Doe", "johndoe@example.org"),
            ...               Person("Mary Sue", "marysue@example.org")]

        :type: :obj:`list` of :class:`podgen.Person`
        :RSS: managingEditor or dc:creator, and itunes:author
        )r(   rB   s    r>   r~   zPodcast.authorsN  s    B ~~r@   c                 \    	 t        |      | _        y # t        $ r t        d|z        w xY w)Nz~Only iterable types can be assigned to authors, %s given. You must put your object in a list, even if there's only one author.)rG   r(   r9   )r:   r~   s     r>   r~   zPodcast.authorsq  sA    	J!']DN 	J ?AHI J J	Js    +c                     | j                   S )a  The publication date for the content in this podcast. You
        probably want to use the default value.

        :Default value: If this is :obj:`None` when the feed is generated, the
           publication date of the episode with the latest publication date
           (which may be in the future) is used. If there are no episodes, the
           publication date is omitted from the feed.

        If you set this to a :obj:`str`, it will be parsed and made into a
        :class:`datetime.datetime` object when assigned. You may also set it to
        a :class:`datetime.datetime` object directly. In any case, the time and
        date must be timezone aware.

        If you want to forcefully omit the publication date from the feed, set
        this to ``False``.

        :type: :class:`datetime.datetime`, :obj:`str` (will be converted to
           and stored as :class:`datetime.datetime`), :obj:`None` for default or
           :obj:`False` to leave out.
        :RSS: pubDate
        )r)   rB   s    r>   r   zPodcast.publication_datez  s    . &&&r@   c                     |e|durat        |t              rt        j                  j	                  |      }t        |t
              st        d      |j                  t        d      || _        y r   )	rF   r   r{   r   r   r
   rM   r   r)   )r:   r   s     r>   r   zPodcast.publication_date  sh    ',<E,I*L9#+??#8#89I#J .9 !:;;!((0 !GHH"2r@   c                     | j                   S )ap  Set of hours of the day in which podcatchers don't need to refresh
        this feed.

        This isn't widely supported by podcatchers.

        The hours are represented as integer values from 0 to 23.
        Note that while the content of the set is checked when it is first
        assigned to ``skip_hours``, further changes to the set "in place" will
        not be checked before you generate the RSS.

        For example, to stop refreshing the feed between 18 and 7::

            >>> from podgen import Podcast
            >>> p = Podcast()
            >>> p.skip_hours = set(range(18, 24))
            >>> p.skip_hours
            {18, 19, 20, 21, 22, 23}
            >>> p.skip_hours |= set(range(8))
            >>> p.skip_hours
            {0, 1, 2, 3, 4, 5, 6, 7, 18, 19, 20, 21, 22, 23}

        :type: :obj:`set` of :obj:`int`
        :RSS: skipHours
        )r*   rB   s    r>   r   zPodcast.skip_hours  s    4    r@   c                     |Mt        |t              st        |t              st        |      }|D ]  }|t        d      vst	        d|z         || _        y )N   zInvalid hour %s)rF   rG   setrangerM   r*   )r:   hoursr   s      r>   r   zPodcast.skip_hours  sU    ud+z%/EE
E"I%$%6%:;;  "r@   c                     | j                   S )a  Set of days in which podcatchers don't need to refresh this feed.

        This isn't widely supported by podcatchers.

        The days are represented using strings of their English names, like
        "Monday" or "wednesday". The day names are automatically capitalized
        when the set is assigned to ``skip_days``, but subsequent changes to the
        set "in place" are only checked and capitalized when the RSS feed is
        generated.

        For example, to stop refreshing the feed in the weekend::

            >>> from podgen import Podcast
            >>> p = Podcast()
            >>> p.skip_days = {"Friday", "Saturday", "sUnDaY"}
            >>> p.skip_days
            {"Saturday", "Friday", "Sunday"}

        :type: :obj:`set` of :obj:`str`
        :RSS: skipDays
        )r+   rB   s    r>   r   zPodcast.skip_days  s    . r@   c                     |Zt        |t              st        |      }|D ]"  }|j                         dvst        d|z         t        d |D              | _        y d | _        y )N)mondaytuesday	wednesdaythursdayfridaysaturdaysundayzInvalid day %sc              3   <   K   | ]  }|j                           y wrE   )
capitalize)r   rk   s     r>   r   z$Podcast.skip_days.<locals>.<genexpr>  s     "Dt3>>#3ts   )rF   r   lowerrM   r+   )r:   daysr   s      r>   r   zPodcast.skip_days  sf    dC(4ywwy %8 8$%5%9::   #"Dt"DDD#Dr@   c                     | j                   S )zThe :class:`~podgen.Person` responsible for
        technical issues relating to the feed.

        :type: :class:`podgen.Person`
        :RSS: webMaster
        )r,   rB   s    r>   r   zPodcast.web_master  s        r@   c                 \    |#t        |d      r|j                  st        d      || _        y )Nr   zLThe webmaster must have an email attribute and it must be set and not empty.)r7   r   rM   r,   )r:   r   s     r>   r   zPodcast.web_master  s7    !J09I9I  "E F F&r@   c                     | j                   S )zThe iTunes category, which appears in the category column
        and in iTunes Store listings.

        :type: :class:`podgen.Category`
        :RSS: itunes:category
        )r/   rB   s    r>   r   zPodcast.category  s     r@   c                 r    |.t        |d      rt        |d      r|| _        y t        d|z        d | _        y )Nr   r   z-A Category(-like) object must be used, got %s)r7   r/   r9   )r:   r   s     r>   r   zPodcast.category  sF    x,Hm4"* !%'/!0 1 1 #DOr@   c                     | j                   S )uC  The URL of the artwork for this podcast. iTunes
        prefers square images that are at least ``1400x1400`` pixels.
        Podcasts with an image smaller than this are *not* eligible to be
        featured on the iTunes Store.

        iTunes supports images in JPEG and PNG formats with an RGB color space
        (CMYK is not supported). The URL must end in ".jpg" or ".png"; if they
        don't, a :class:`.NotSupportedByItunesWarning` will be issued.

        :type: :obj:`str`
        :RSS: itunes:image

        .. note::

           If you change your podcast’s image, you must also change the file’s
           name; iTunes doesn't check the image to see if it has changed.

           Additionally, the server hosting your cover art image must allow HTTP
           HEAD requests (most servers support this).
        )r0   rB   s    r>   r   zPodcast.image  s    , ||r@   c                     |Z|j                         }|j                  d      s1t        j                  d|j	                  d      d   z  t
        d       || _        y d | _        y )N)z.jpgz.jpegz.pngz*Image URL must end with png or jpg, not %sr      )
stacklevel)r   endswithwarningswarnsplitr   r0   )r:   r   lowercase_itunes_images      r>   r   zPodcast.image+  s`    %*[[]"*334MN ;;s+B/01L 	 !DLDLr@   c                     | j                   S )aX  Whether this podcast is completed or not.

        If you set this to ``True``, you are indicating that no more
        episodes will be added to the podcast. If you let this be ``None`` or
        ``False``, you are indicating that new episodes may be posted.

        :type: :obj:`bool`
        :RSS: itunes:complete

        .. warning::

            Setting this to ``True`` is the same as promising you'll never ever
            release a new episode. Do NOT set this to ``True`` as long as
            there's any chance AT ALL that a new episode will be released
            someday.

        )r1   rB   s    r>   r   zPodcast.complete:  s    & r@   c                 8    |t        |      | _        y d | _        y rE   )boolr1   )r:   r   s     r>   r   zPodcast.completeO  s    "8nDO"DOr@   c                     | j                   S )a  The :class:`~podgen.Person` who owns this podcast. iTunes
        will use this person's name and email address for all correspondence
        related to this podcast. It will not be publicly displayed, but it's
        still publicly available in the RSS source.

        Both the name and email are required.

        :type: :class:`podgen.Person`
        :RSS: itunes:owner
        )r3   rB   s    r>   r   zPodcast.ownerV  s     ||r@   c                 l    |+|j                   r|j                  r|| _        y t        d      d | _        y )Nz Both name and email must be set.)r   r   r3   rM   )r:   r   s     r>   r   zPodcast.ownerd  s/    zzekk$ !CDDDLr@   c                     | j                   S )a2  The URL which this feed is available at.

        Identifying a feed's URL within the feed makes it more portable,
        self-contained, and easier to cache. You should therefore set this
        attribute if you're able to.

        :type: :obj:`str`
        :RSS: atom:link with ``rel="self"``
        )r-   rB   s    r>   r   zPodcast.feed_urln  s     r@   c                 R    ||r|j                  d      st        d      || _        y )N)zhttp://zhttps://zftp://znews://zoThe feed url must be a valid URL, but it doesn't have a valid URL scheme (like for example http:// or https://))
startswithrM   r-   )r:   r   s     r>   r   zPodcast.feed_url{  s=     3 3 5 ! 
 ! "J K K #r@   rE   )Fr   T)NNNF)NNN)!__name__
__module____qualname____doc__r?   propertyrC   setterrI   rR   r   r   r   r   r   r   r   r   ry   r\   r   r   r$   r~   r   r   r   r   r   r   r   r   r    r@   r>   r   r   %   s   %Nv4r 	 	 __ 
 &$ &$P A A:dL#(- 07 $. +037*QX("$ # #( 
/ 
/  @ \\     ?C%*?2-
 % %    D ^^J J ' '0 3 3 ! !6 " "    0 
$ 
$ ! ! ' '   __
# 
#  . \\     ( __# #   \\    
 
 __
# 
#r@   r   )'r  
__future__r   r   r   r   builtinsfuture.utilsr   lxmlr	   r
   dateutil.parserr{   dateutil.tzpodgen.episoder   podgen.warningsr   podgen.utilr   r   r   r   podgen.personr   podgen.versionr   syspodgen.compatr   collectionsrK   r  rU   version_str_feedgen_versionobjectr   r  r@   r>   <module>r+     sj   	 S R  "     " 7     
 &    >>-- a#f a#r@   