Beispiel #1
0
def main():
    with open('thebugle.json') as f:
        episodes = json.load(f)

    p = Podcast(
        name="TimesOnLine Bugle Archive",
        description="Old Bugle episodes, podcast feed",
        website="https://www.thebuglepodcast.com/",
        explicit=False,
    )

    for episode in episodes:
        ep = p.add_episode(
            Episode(title=f"{episode['id']}: {episode['title']}"))
        ep.media = Media.create_from_server_response(
            f"{MEDIA_BASE_URL}/{episode['file']}")

        ep.media.fetch_duration()

        date = episode['date'].split('-')
        ep.publication_date = datetime(int(date[0]),
                                       int(date[1]),
                                       int(date[2]),
                                       0,
                                       0,
                                       0,
                                       tzinfo=pytz.utc)

    print(p.rss_str())
Beispiel #2
0
def main():
    """Create an example podcast and print it or save it to a file."""
    # There must be exactly one argument, and it is must end with rss
    if len(sys.argv) != 2 or not (
            sys.argv[1].endswith('rss')):
        # Invalid usage, print help message
        # print_enc is just a custom function which functions like print,
        # except it deals with byte arrays properly.
        print_enc ('Usage: %s ( <file>.rss | rss )' % \
                'python -m podgen')
        print_enc ('')
        print_enc ('  rss              -- Generate RSS test output and print it to stdout.')
        print_enc ('  <file>.rss       -- Generate RSS test teed and write it to file.rss.')
        print_enc ('')
        exit()

    # Remember what type of feed the user wants
    arg = sys.argv[1]

    from podgen import Podcast, Person, Media, Category, htmlencode
    # Initialize the feed
    p = Podcast()
    p.name = 'Testfeed'
    p.authors.append(Person("Lars Kiesow", "*****@*****.**"))
    p.website = 'http://example.com'
    p.copyright = 'cc-by'
    p.description = 'This is a cool feed!'
    p.language = 'de'
    p.feed_url = 'http://example.com/feeds/myfeed.rss'
    p.category = Category('Technology', 'Podcasting')
    p.explicit = False
    p.complete = False
    p.new_feed_url = 'http://example.com/new-feed.rss'
    p.owner = Person('John Doe', '*****@*****.**')
    p.xslt = "http://example.com/stylesheet.xsl"

    e1 = p.add_episode()
    e1.id = 'http://lernfunk.de/_MEDIAID_123#1'
    e1.title = 'First Element'
    e1.summary = htmlencode('''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tamen
            aberramus a proposito, et, ne longius, prorsus, inquam, Piso, si ista
            mala sunt, placet. Aut etiam, ut vestitum, sic sententiam habeas aliam
            domesticam, aliam forensem, ut in fronte ostentatio sit, intus veritas
            occultetur? Cum id fugiunt, re eadem defendunt, quae Peripatetici,
            verba <3.''')
    e1.link = 'http://example.com'
    e1.authors = [Person('Lars Kiesow', '*****@*****.**')]
    e1.publication_date = datetime.datetime(2014, 5, 17, 13, 37, 10, tzinfo=pytz.utc)
    e1.media = Media("http://example.com/episodes/loremipsum.mp3", 454599964,
                     duration=
                     datetime.timedelta(hours=1, minutes=32, seconds=19))

    # Should we just print out, or write to file?
    if arg == 'rss':
        # Print
        print_enc(p.rss_str())
    elif arg.endswith('rss'):
        # Write to file
        p.rss_file(arg, minimize=True)
Beispiel #3
0
def generate_podcast_xml(podcasts):
    podcast = Podcast(name=config.PODCAST_NAME,
                      description=config.PODCAST_DESCRIPTION,
                      website=config.PODCAST_WEBSITE,
                      explicit=config.PODCAST_CONTAINS_EXPLICIT_CONTENT,
                      withhold_from_itunes=True)
    podcast.episodes = podcasts
    return podcast.rss_str()
Beispiel #4
0
def main(event, context):
    dynamodb = boto3.resource('dynamodb', region_name='sa-east-1')

    table = dynamodb.Table('semservidor-dev')

    podcasts = table.scan()

    author = Person("Evandro Pires da Silva", "*****@*****.**")
    p = Podcast(
        name="Sem Servidor",
        description=
        "Podcast dedicado a arquitetura serverless, com conteúdo de qualidade em português.",
        website="https://semservidor.com.br",
        explicit=False,
        copyright="2020 Evandro Pires da Silva",
        language="pr-BR",
        authors=[author],
        feed_url=
        "https://3tz8r90j0d.execute-api.sa-east-1.amazonaws.com/dev/podcasts/rss",
        category=Category("Music", "Music History"),
        owner=author,
        image="http://d30gvsirhz3ono.cloudfront.net/logo_semservidor_teste.jpg",
        web_master=Person(None, "*****@*****.**"))

    items = podcasts['Items']
    for item in items:
        base_url = "http://d30gvsirhz3ono.cloudfront.net/"
        file_path = base_url + item['info']['arquivo']['nome']
        p.episodes += [
            Episode(title=item['info']['episodio'],
                    media=Media(file_path,
                                int(item['info']['arquivo']['tamanho'])),
                    summary=item['info']['descricao'],
                    position=int(item['id']))
        ]

    p.apply_episode_order()
    rss = p.rss_str()

    response = {
        "statusCode": 200,
        "headers": {
            "content-type": "application/xml"
        },
        "body": rss
    }

    return response
Beispiel #5
0
def scrape_by_program(program,
                      web_session=requests_html.HTMLSession(),
                      params=params):
    podcast = Podcast()
    podcast.explicit = False
    podcast.website = params[PARAMS_BASEURL].format(program=program)

    if program == 'morning-edition':
        podcast.name = "NPR Morning Edition"
        podcast.description = \
            """Every weekday for over three decades, Morning Edition has taken
            listeners around the country and the world with two hours of multi-faceted
            stories and commentaries that inform, challenge and occasionally amuse.
            Morning Edition is the most listened-to news radio program in the country."""
        podcast.image = 'https://media.npr.org/assets/img/2018/08/06/npr_me_podcasttile_sq-4036eb96471eeed96c37dfba404bb48ea798e78c-s200-c85.jpg'

    elif program == 'all-things-considered':
        podcast.name = "NPR All Things Considered"
        podcast.description = \
            """NPR's afternoon news show"""
        podcast.image = 'https://media.npr.org/assets/img/2018/08/06/npr_atc_podcasttile_sq-bcc33a301405d37aa6bdcc090f43d29264915f4a-s200-c85.jpg'

    elif program == 'weekend-edition-saturday':
        podcast.name = "NPR Weekend Edition Saturday"
        podcast.description = \
            """NPR morning news on Saturday"""
        podcast.image = 'https://media.npr.org/assets/img/2019/02/26/we_otherentitiestemplatesat_sq-cbde87a2fa31b01047441e6f34d2769b0287bcd4-s200-c85.png'

    elif program == 'weekend-edition-sunday':
        podcast.name = "NPR Weekend Edition Sunday"
        podcast.description = \
            """NPR morning news show on Sunday"""
        podcast.image = 'https://media.npr.org/assets/img/2019/02/26/we_otherentitiestemplatesun_sq-4a03b35e7e5adfa446aec374523a578d54dc9bf5-s200-c85.png'

    else:
        raise WebFormatException(f"program { program } not found")

    scrape(web_session, params, program, podcast)

    rssfeed = podcast.rss_str(minimize=False)
    #log.debug(f"\n\nfeed { rssfeed }")

    return rssfeed
Beispiel #6
0
def scrape_morning_edition(
        web_session=requests_html.HTMLSession(), params=params):

    podcast = Podcast()
    podcast.name = "NPR Morning Edition"
    podcast.description = \
        """Every weekday for over three decades, Morning Edition has taken
        listeners around the country and the world with two hours of multi-faceted
        stories and commentaries that inform, challenge and occasionally amuse.
        Morning Edition is the most listened-to news radio program in the country."""
    podcast.website = "https://www.npr.org/programs/morning-edition"
    podcast.explicit = False

    scrape(web_session, params, 'morning-edition', podcast)

    rssfeed = podcast.rss_str(minimize=False)
    #log.debug(f"\n\nfeed { rssfeed }")

    return rssfeed
Beispiel #7
0
    def generate_podcast(self, feed_name: str) -> str:
        """
        Create podcast XML based on the files found in podcastDir. Taken from
        https://podgen.readthedocs.io/en/latest/usage_guide/podcasts.html

        :param self: PodcastService class
        :param feed_name: name of the feed and the sub-directory for files
        :return:  string of the podcast
        """
        # Initialize the feed
        p = Podcast()

        # Required fields
        p.name = f'{feed_name} Archive'
        p.description = 'Stuff to listen to later'
        p.website = self.base_url
        p.complete = False

        # Optional
        p.language = 'en-US'
        p.feed_url = f'{p.website}/feeds/{feed_name}/rss'
        p.explicit = False
        p.authors.append(Person("Anthology"))

        # for filepath in glob.iglob(f'{self.search_dir}/{feed_name}/*.mp3'):
        for path in Path(f'{self.search_dir}/{feed_name}').glob('**/*.mp3'):
            filepath = str(path)
            episode = p.add_episode()

            # Attempt to load saved metadata
            metadata_file_name = filepath.replace('.mp3', '.json')
            try:
                with open(metadata_file_name) as metadata_file:
                    metadata = json.load(metadata_file)
            except FileNotFoundError:
                metadata = {}
            except JSONDecodeError:
                metadata = {}
                self.logger.error(f'Failed to read {metadata_file_name}')

            # Build the episode based on either the saved metadata or the file details
            episode.title = metadata.get(
                'title',
                filepath.split('/')[-1].rstrip('.mp3'))
            episode.summary = metadata.get('summary',
                                           htmlencode('Some Summary'))
            if 'link' in metadata:
                episode.link = metadata.get('link')
            if 'authors' in metadata:
                episode.authors = [
                    Person(author) for author in metadata.get('authors')
                ]
            episode.publication_date = \
                isoparse(metadata.get('publication_date')) if 'publication_date' in metadata \
                else datetime.fromtimestamp(os.path.getmtime(filepath), tz=pytz.utc)
            episode.media = Media(
                f'{p.website}/{filepath.lstrip(self.search_dir)}'.replace(
                    ' ', '+'), os.path.getsize(filepath))
            episode.media.populate_duration_from(filepath)

            if "image" in metadata:
                episode.image = metadata.get('image')
            else:
                for ext in ['.jpg', '.png']:
                    image_file_name = filepath.replace('.mp3', ext)
                    if os.path.isfile(image_file_name):
                        episode.image = f'{p.website}/{image_file_name.lstrip(self.search_dir)}'.replace(
                            ' ', '+')
                        break

            # Save the metadata for future editing
            if not os.path.exists(metadata_file_name):
                metadata = {
                    'title': episode.title,
                    'summary': episode.summary,
                    'publication_date': episode.publication_date,
                    'authors': episode.authors
                }
                with open(metadata_file_name, 'w') as outFile:
                    json.dump(metadata, outFile, indent=2, default=str)

        return p.rss_str()
Beispiel #8
0
class TestPodcast(unittest.TestCase):
    def setUp(self):
        self.existing_locale = locale.setlocale(locale.LC_ALL, None)
        locale.setlocale(locale.LC_ALL, 'C')

        fg = Podcast()

        self.nsContent = "http://purl.org/rss/1.0/modules/content/"
        self.nsDc = "http://purl.org/dc/elements/1.1/"
        self.nsItunes = "http://www.itunes.com/dtds/podcast-1.0.dtd"
        self.feed_url = "http://example.com/feeds/myfeed.rss"

        self.name = 'Some Testfeed'

        # Use character not in ASCII to catch encoding errors
        self.author = Person('Jon Døll', '*****@*****.**')

        self.website = 'http://example.com'
        self.description = 'This is a cool feed!'
        self.subtitle = 'Coolest of all'

        self.language = 'en'

        self.cloudDomain = 'example.com'
        self.cloudPort = '4711'
        self.cloudPath = '/ws/example'
        self.cloudRegisterProcedure = 'registerProcedure'
        self.cloudProtocol = 'SOAP 1.1'

        self.pubsubhubbub = "http://pubsubhubbub.example.com/"

        self.contributor = {
            'name': "Contributor Name",
            'email': 'Contributor email'
        }
        self.copyright = "The copyright notice"
        self.docs = 'http://www.rssboard.org/rss-specification'
        self.skip_days = set(['Tuesday'])
        self.skip_hours = set([23])

        self.explicit = False

        self.programname = podgen.version.name

        self.web_master = Person(email='*****@*****.**')
        self.image = "http://example.com/static/podcast.png"
        self.owner = self.author
        self.complete = True
        self.new_feed_url = "https://example.com/feeds/myfeed2.rss"
        self.xslt = "http://example.com/feed/stylesheet.xsl"

        fg.name = self.name
        fg.website = self.website
        fg.description = self.description
        fg.subtitle = self.subtitle
        fg.language = self.language
        fg.cloud = (self.cloudDomain, self.cloudPort, self.cloudPath,
                    self.cloudRegisterProcedure, self.cloudProtocol)
        fg.pubsubhubbub = self.pubsubhubbub
        fg.copyright = self.copyright
        fg.authors.append(self.author)
        fg.skip_days = self.skip_days
        fg.skip_hours = self.skip_hours
        fg.web_master = self.web_master
        fg.feed_url = self.feed_url
        fg.explicit = self.explicit
        fg.image = self.image
        fg.owner = self.owner
        fg.complete = self.complete
        fg.new_feed_url = self.new_feed_url
        fg.xslt = self.xslt

        self.fg = fg

        warnings.simplefilter("always")

        def noop(*args, **kwargs):
            pass

        warnings.showwarning = noop

    def tearDown(self):
        locale.setlocale(locale.LC_ALL, self.existing_locale)

    def test_constructor(self):
        # Overwrite fg from setup
        self.fg = Podcast(
            name=self.name,
            website=self.website,
            description=self.description,
            subtitle=self.subtitle,
            language=self.language,
            cloud=(self.cloudDomain, self.cloudPort, self.cloudPath,
                   self.cloudRegisterProcedure, self.cloudProtocol),
            pubsubhubbub=self.pubsubhubbub,
            copyright=self.copyright,
            authors=[self.author],
            skip_days=self.skip_days,
            skip_hours=self.skip_hours,
            web_master=self.web_master,
            feed_url=self.feed_url,
            explicit=self.explicit,
            image=self.image,
            owner=self.owner,
            complete=self.complete,
            new_feed_url=self.new_feed_url,
            xslt=self.xslt,
        )
        # Test that the fields are actually set
        self.test_baseFeed()

    def test_constructorUnknownAttributes(self):
        self.assertRaises(TypeError, Podcast, naem="Oh, looks like a typo")
        self.assertRaises(TypeError, Podcast, "Haha, No Keyword")

    def test_baseFeed(self):
        fg = self.fg

        assert fg.name == self.name

        assert fg.authors[0] == self.author
        assert fg.web_master == self.web_master

        assert fg.website == self.website

        assert fg.description == self.description
        assert fg.subtitle == self.subtitle

        assert fg.language == self.language
        assert fg.feed_url == self.feed_url
        assert fg.image == self.image
        assert fg.owner == self.owner
        assert fg.complete == self.complete
        assert fg.pubsubhubbub == self.pubsubhubbub
        assert fg.cloud == (self.cloudDomain, self.cloudPort, self.cloudPath,
                            self.cloudRegisterProcedure, self.cloudProtocol)
        assert fg.copyright == self.copyright
        assert fg.new_feed_url == self.new_feed_url
        assert fg.skip_days == self.skip_days
        assert fg.skip_hours == self.skip_hours
        assert fg.xslt == self.xslt

    def test_rssFeedFile(self):
        fg = self.fg
        rssString = self.getRssFeedFileContents(fg, xml_declaration=False)\
            .replace('\n', '')
        self.checkRssString(rssString)

    def getRssFeedFileContents(self, fg, **kwargs):
        # Keep track of our temporary file and its filename
        filename = None
        file = None
        encoding = 'UTF-8'
        try:
            # Get our temporary file name
            file = tempfile.NamedTemporaryFile(delete=False)
            filename = file.name
            # Close the file; we will just use its name
            file.close()
            # Write the RSS to the file (overwriting it)
            fg.rss_file(filename=filename, encoding=encoding, **kwargs)
            # Read the resulting RSS
            with open(filename, "r", encoding=encoding) as myfile:
                rssString = myfile.read()
        finally:
            # We don't need the file any longer, so delete it
            if filename:
                os.unlink(filename)
            elif file:
                # Ops, we were interrupted between the first and second stmt
                filename = file.name
                file.close()
                os.unlink(filename)
            else:
                # We were interrupted between entering the try-block and
                # getting the temporary file. Not much we can do.
                pass
        return rssString

    def test_rssFeedString(self):
        fg = self.fg
        rssString = fg.rss_str(xml_declaration=False)
        self.checkRssString(rssString)

    def test_rssStringAndFileAreEqual(self):
        rss_string = self.fg.rss_str()
        rss_file = self.getRssFeedFileContents(self.fg)
        self.assertEqual(rss_string, rss_file)

    def checkRssString(self, rssString):
        feed = etree.fromstring(rssString)
        nsRss = self.nsContent
        nsAtom = "http://www.w3.org/2005/Atom"

        channel = feed.find("channel")
        assert channel != None

        assert channel.find("title").text == self.name
        assert channel.find("description").text == self.description
        assert channel.find("{%s}subtitle" % self.nsItunes).text == \
            self.subtitle
        assert channel.find("link").text == self.website
        assert channel.find("lastBuildDate").text != None
        assert channel.find("language").text == self.language
        assert channel.find(
            "docs").text == "http://www.rssboard.org/rss-specification"
        assert self.programname in channel.find("generator").text
        assert channel.find("cloud").get('domain') == self.cloudDomain
        assert channel.find("cloud").get('port') == self.cloudPort
        assert channel.find("cloud").get('path') == self.cloudPath
        assert channel.find("cloud").get(
            'registerProcedure') == self.cloudRegisterProcedure
        assert channel.find("cloud").get('protocol') == self.cloudProtocol
        assert channel.find("copyright").text == self.copyright
        assert channel.find("docs").text == self.docs
        assert self.author.email in channel.find("managingEditor").text
        assert channel.find("skipDays").find("day").text in self.skip_days
        assert int(
            channel.find("skipHours").find("hour").text) in self.skip_hours
        assert self.web_master.email in channel.find("webMaster").text

        links = channel.findall("{%s}link" % nsAtom)
        selflinks = [link for link in links if link.get('rel') == 'self']
        hublinks = [link for link in links if link.get('rel') == 'hub']

        assert selflinks, "No <atom:link rel='self'> element found"
        selflink = selflinks[0]
        assert selflink.get('href') == self.feed_url
        assert selflink.get('type') == 'application/rss+xml'

        assert hublinks, "No <atom:link rel='hub'> element found"
        hublink = hublinks[0]
        assert hublink.get('href') == self.pubsubhubbub
        assert hublink.get('type') is None

        assert channel.find("{%s}image" % self.nsItunes).get('href') == \
            self.image
        owner = channel.find("{%s}owner" % self.nsItunes)
        assert owner.find("{%s}name" % self.nsItunes).text == self.owner.name
        assert owner.find("{%s}email" % self.nsItunes).text == self.owner.email
        assert channel.find("{%s}complete" % self.nsItunes).text.lower() == \
            "yes"
        assert channel.find("{%s}new-feed-url" % self.nsItunes).text == \
            self.new_feed_url

    def test_feedUrlValidation(self):
        self.assertRaises(ValueError, setattr, self.fg, "feed_url",
                          "example.com/feed.rss")

    def test_generator(self):
        software_name = "My Awesome Software"
        software_version = (1, 0)
        software_url = "http://example.com/awesomesoft/"

        # Using set_generator, text includes python-podgen
        self.fg.set_generator(software_name)
        rss = self.fg._create_rss()
        generator = rss.find("channel").find("generator").text
        assert software_name in generator
        assert self.programname in generator

        # Using set_generator, text excludes python-podgen
        self.fg.set_generator(software_name, exclude_podgen=True)
        generator = self.fg._create_rss().find("channel").find(
            "generator").text
        assert software_name in generator
        assert self.programname not in generator

        # Using set_generator, text includes name, version and url
        self.fg.set_generator(software_name, software_version, software_url)
        generator = self.fg._create_rss().find("channel").find(
            "generator").text
        assert software_name in generator
        assert str(software_version[0]) in generator
        assert str(software_version[1]) in generator
        assert software_url in generator

        # Using generator directly, text excludes python-podgen
        self.fg.generator = software_name
        generator = self.fg._create_rss().find("channel").find(
            "generator").text
        assert software_name in generator
        assert self.programname not in generator

    def test_str(self):
        assert str(self.fg) == self.fg.rss_str(minimize=False,
                                               encoding="UTF-8",
                                               xml_declaration=True)

    def test_updated(self):
        date = datetime.datetime(2016, 1, 1, 0, 10, tzinfo=dateutil.tz.tzutc())

        def getLastBuildDateElement(fg):
            return fg._create_rss().find("channel").find("lastBuildDate")

        # Test that it has a default
        assert getLastBuildDateElement(self.fg) is not None

        # Test that it respects my custom value
        self.fg.last_updated = date
        lastBuildDate = getLastBuildDateElement(self.fg)
        assert lastBuildDate is not None
        assert dateutil.parser.parse(lastBuildDate.text) == date

        # Test that it is left out when set to False
        self.fg.last_updated = False
        lastBuildDate = getLastBuildDateElement(self.fg)
        assert lastBuildDate is None

    def test_AuthorEmail(self):
        # Just email - so use managingEditor, not dc:creator or itunes:author
        # This is per the RSS best practices, see the section about dc:creator
        self.fg.authors = [Person(None, "*****@*****.**")]
        channel = self.fg._create_rss().find("channel")
        # managingEditor uses email?
        assert channel.find("managingEditor").text == self.fg.authors[0].email
        # No dc:creator?
        assert channel.find("{%s}creator" % self.nsDc) is None
        # No itunes:author?
        assert channel.find("{%s}author" % self.nsItunes) is None

    def test_AuthorName(self):
        # Just name - use dc:creator and itunes:author, not managingEditor
        self.fg.authors = [Person("Just a. Name")]
        channel = self.fg._create_rss().find("channel")
        # No managingEditor?
        assert channel.find("managingEditor") is None
        # dc:creator equals name?
        assert channel.find("{%s}creator" % self.nsDc).text == \
               self.fg.authors[0].name
        # itunes:author equals name?
        assert channel.find("{%s}author" % self.nsItunes).text == \
            self.fg.authors[0].name

    def test_AuthorNameAndEmail(self):
        # Both name and email - use managingEditor and itunes:author,
        # not dc:creator
        self.fg.authors = [Person("Both a name", "*****@*****.**")]
        channel = self.fg._create_rss().find("channel")
        # Does managingEditor follow the pattern "email (name)"?
        self.assertEqual(
            self.fg.authors[0].email + " (" + self.fg.authors[0].name + ")",
            channel.find("managingEditor").text)
        # No dc:creator?
        assert channel.find("{%s}creator" % self.nsDc) is None
        # itunes:author uses name only?
        assert channel.find("{%s}author" % self.nsItunes).text == \
            self.fg.authors[0].name

    def test_multipleAuthors(self):
        # Multiple authors - use itunes:author and dc:creator, not
        # managingEditor.

        person1 = Person("Multiple", "*****@*****.**")
        person2 = Person("Are", "*****@*****.**")
        self.fg.authors = [person1, person2]
        channel = self.fg._create_rss().find("channel")

        # Test dc:creator
        author_elements = \
            channel.findall("{%s}creator" % self.nsDc)
        author_texts = [e.text for e in author_elements]

        assert len(author_texts) == 2
        assert person1.name in author_texts[0]
        assert person1.email in author_texts[0]
        assert person2.name in author_texts[1]
        assert person2.email in author_texts[1]

        # Test itunes:author
        itunes_author = channel.find("{%s}author" % self.nsItunes)
        assert itunes_author is not None
        itunes_author_text = itunes_author.text
        assert person1.name in itunes_author_text
        assert person1.email not in itunes_author_text
        assert person2.name in itunes_author_text
        assert person2.email not in itunes_author_text

        # Test that managingEditor is not used
        assert channel.find("managingEditor") is None

    def test_authorsInvalidValue(self):
        self.assertRaises(TypeError, self.do_authorsInvalidValue)

    def do_authorsInvalidValue(self):
        self.fg.authors = Person("Opsie", "*****@*****.**")

    def test_webMaster(self):
        self.fg.web_master = Person(None, "*****@*****.**")
        channel = self.fg._create_rss().find("channel")
        assert channel.find("webMaster").text == self.fg.web_master.email

        self.assertRaises(ValueError, setattr, self.fg, "web_master",
                          Person("Mr. No Email Address"))

        self.fg.web_master = Person("Both a name", "*****@*****.**")
        channel = self.fg._create_rss().find("channel")
        # Does webMaster follow the pattern "email (name)"?
        self.assertEqual(
            self.fg.web_master.email + " (" + self.fg.web_master.name + ")",
            channel.find("webMaster").text)

    def test_categoryWithoutSubcategory(self):
        c = Category("Arts")
        self.fg.category = c
        channel = self.fg._create_rss().find("channel")
        itunes_category = channel.find("{%s}category" % self.nsItunes)
        assert itunes_category is not None

        self.assertEqual(itunes_category.get("text"), c.category)

        assert itunes_category.find("{%s}category" % self.nsItunes) is None

    def test_categoryWithSubcategory(self):
        c = Category("Arts", "Food")
        self.fg.category = c
        channel = self.fg._create_rss().find("channel")
        itunes_category = channel.find("{%s}category" % self.nsItunes)
        assert itunes_category is not None
        itunes_subcategory = itunes_category\
            .find("{%s}category" % self.nsItunes)
        assert itunes_subcategory is not None
        self.assertEqual(itunes_subcategory.get("text"), c.subcategory)

    def test_categoryChecks(self):
        c = ("Arts", "Food")
        self.assertRaises(TypeError, setattr, self.fg, "category", c)

    def test_explicitIsExplicit(self):
        self.fg.explicit = True
        channel = self.fg._create_rss().find("channel")
        itunes_explicit = channel.find("{%s}explicit" % self.nsItunes)
        assert itunes_explicit is not None
        assert itunes_explicit.text.lower() in ("yes", "explicit", "true"),\
            "itunes:explicit was %s, expected yes, explicit or true" \
            % itunes_explicit.text

    def test_explicitIsClean(self):
        self.fg.explicit = False
        channel = self.fg._create_rss().find("channel")
        itunes_explicit = channel.find("{%s}explicit" % self.nsItunes)
        assert itunes_explicit is not None
        assert itunes_explicit.text.lower() in ("no", "clean", "false"),\
            "itunes:explicit was %s, expected no, clean or false" \
            % itunes_explicit.text

    def test_mandatoryValues(self):
        # Try to create a Podcast once for each mandatory property.
        # On each iteration, exactly one of the properties is not set.
        # Therefore, an exception should be thrown on each iteration.
        mandatory_properties = set([
            "description",
            "title",
            "link",
            "explicit",
        ])

        for test_property in mandatory_properties:
            fg = Podcast()
            if test_property != "description":
                fg.description = self.description
            if test_property != "title":
                fg.name = self.name
            if test_property != "link":
                fg.website = self.website
            if test_property != "explicit":
                fg.explicit = self.explicit
            try:
                self.assertRaises(ValueError, fg._create_rss)
            except AssertionError as e:
                raise_from(
                    AssertionError("The test failed for %s" % test_property),
                    e)

    def test_withholdFromItunesOffByDefault(self):
        assert not self.fg.withhold_from_itunes

    def test_withholdFromItunes(self):
        self.fg.withhold_from_itunes = True
        itunes_block = self.fg._create_rss().find("channel")\
            .find("{%s}block" % self.nsItunes)
        assert itunes_block is not None
        self.assertEqual(itunes_block.text.lower(), "yes")

        self.fg.withhold_from_itunes = False
        itunes_block = self.fg._create_rss().find("channel")\
            .find("{%s}block" % self.nsItunes)
        assert itunes_block is None

    def test_modifyingSkipDaysAfterwards(self):
        self.fg.skip_days.add("Unrecognized day")
        self.assertRaises(ValueError, self.fg.rss_str)
        self.fg.skip_days.remove("Unrecognized day")
        self.fg.rss_str()  # Now it works

    def test_modifyingSkipHoursAfterwards(self):
        self.fg.skip_hours.add(26)
        self.assertRaises(ValueError, self.fg.rss_str)
        self.fg.skip_hours.remove(26)
        self.fg.rss_str()  # Now it works

    # Tests for xslt
    def test_xslt_str(self):
        def use_str(**kwargs):
            return self.fg.rss_str(**kwargs)

        self.help_test_xslt_using(use_str)

    def test_xslt_file(self):
        def use_file(**kwargs):
            return self.getRssFeedFileContents(self.fg, **kwargs)

        self.help_test_xslt_using(use_file)

    def help_test_xslt_using(self, generated_feed):
        """Run tests for xslt, generating the feed str using the given function.
        """
        xslt_path = "http://example.com/mystylesheet.xsl"
        xslt_pi = "<?xml-stylesheet"

        # No xslt when set to None
        self.fg.xslt = None
        assert xslt_pi not in generated_feed()
        assert xslt_pi not in generated_feed(minimize=True)
        assert xslt_pi not in generated_feed(xml_declaration=False)

        self.fg.xslt = xslt_path

        # Now we have the stylesheet in there
        assert xslt_pi in generated_feed()
        assert xslt_pi in generated_feed(minimize=True)
        assert xslt_pi in generated_feed(xml_declaration=False)

        assert xslt_path in generated_feed()
        assert xslt_path in generated_feed(minimize=True)
        assert xslt_path in generated_feed(xml_declaration=False)

    def test_imageWarningNoExt(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            self.assertEqual(len(w), 0)

            # Set image to a URL without proper file extension
            no_ext = "http://static.example.com/images/logo"
            self.fg.image = no_ext
            # Did we get a warning?
            self.assertEqual(1, len(w))
            assert issubclass(w.pop().category, NotSupportedByItunesWarning)
            # Was the image set?
            self.assertEqual(no_ext, self.fg.image)

    def test_imageWarningBadExt(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")

            # Set image to a URL with an unsupported file extension
            bad_ext = "http://static.example.com/images/logo.gif"
            self.fg.image = bad_ext
            # Did we get a warning?
            self.assertEqual(1, len(w))
            # Was it of the correct type?
            assert issubclass(w.pop().category, NotSupportedByItunesWarning)
            # Was the image still set?
            self.assertEqual(bad_ext, self.fg.image)

    def test_imageNoWarningWithGoodExt(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")

            # Set image to a URL with a supported file extension
            extensions = ["jpg", "png", "jpeg"]
            for extension in extensions:
                good_ext = "http://static.example.com/images/logo." + extension
                self.fg.image = good_ext
                # Did we get no warning?
                self.assertEqual(
                    0, len(w),
                    "Extension %s raised warnings (%s)" % (extension, w))
                # Was the image set?
                self.assertEqual(good_ext, self.fg.image)
    if '\n' in title:
       title = title.split('\n')[-1]
    episodes.append(
       Episode(
          title=title.title(),
          media=Media(media_url, head.headers["Content-Length"]),
          summary=soup.title.text,
          publication_date=arrow.get(date, "DD MMM. YYYY", locale="pt").datetime
       )
   )


# Add some episodes
p.episodes = episodes
# Generate the RSS feed
rss = p.rss_str()

filename = "alta-tensao.xml"

with open(filename, "wb") as f:
   f.write(rss.encode())

session = boto3.session.Session()
client = session.client(
   's3',
   region_name=REGION,
   endpoint_url=f'https://{REGION}.digitaloceanspaces.com',
   aws_access_key_id=ACCESS_KEY,
   aws_secret_access_key=ACCESS_SECRET
)
Beispiel #10
0
class TestPodcast(unittest.TestCase):

    def setUp(self):

        fg = Podcast()

        self.nsContent = "http://purl.org/rss/1.0/modules/content/"
        self.nsDc = "http://purl.org/dc/elements/1.1/"
        self.nsItunes = "http://www.itunes.com/dtds/podcast-1.0.dtd"
        self.feed_url = "http://example.com/feeds/myfeed.rss"

        self.name = 'Some Testfeed'

        self.author = Person('John Doe', '*****@*****.**')

        self.website = 'http://example.com'
        self.description = 'This is a cool feed!'
        self.subtitle = 'Coolest of all'

        self.language = 'en'

        self.cloudDomain = 'example.com'
        self.cloudPort = '4711'
        self.cloudPath = '/ws/example'
        self.cloudRegisterProcedure = 'registerProcedure'
        self.cloudProtocol = 'SOAP 1.1'

        self.pubsubhubbub = "http://pubsubhubbub.example.com/"

        self.contributor = {'name':"Contributor Name",
                            'email': 'Contributor email'}
        self.copyright = "The copyright notice"
        self.docs = 'http://www.rssboard.org/rss-specification'
        self.skip_days = set(['Tuesday'])
        self.skip_hours = set([23])

        self.explicit = False

        self.programname = podgen.version.name

        self.web_master = Person(email='*****@*****.**')
        self.image = "http://example.com/static/podcast.png"
        self.owner = self.author
        self.complete = True
        self.new_feed_url = "https://example.com/feeds/myfeed2.rss"
        self.xslt = "http://example.com/feed/stylesheet.xsl"


        fg.name = self.name
        fg.website = self.website
        fg.description = self.description
        fg.subtitle = self.subtitle
        fg.language = self.language
        fg.cloud = (self.cloudDomain, self.cloudPort, self.cloudPath,
                    self.cloudRegisterProcedure, self.cloudProtocol)
        fg.pubsubhubbub = self.pubsubhubbub
        fg.copyright = self.copyright
        fg.authors.append(self.author)
        fg.skip_days = self.skip_days
        fg.skip_hours = self.skip_hours
        fg.web_master = self.web_master
        fg.feed_url = self.feed_url
        fg.explicit = self.explicit
        fg.image = self.image
        fg.owner = self.owner
        fg.complete = self.complete
        fg.new_feed_url = self.new_feed_url
        fg.xslt = self.xslt

        self.fg = fg

        warnings.simplefilter("always")
        def noop(*args, **kwargs):
            pass
        warnings.showwarning = noop

    def test_constructor(self):
        # Overwrite fg from setup
        self.fg = Podcast(
            name=self.name,
            website=self.website,
            description=self.description,
            subtitle=self.subtitle,
            language=self.language,
            cloud=(self.cloudDomain, self.cloudPort, self.cloudPath,
                   self.cloudRegisterProcedure, self.cloudProtocol),
            pubsubhubbub=self.pubsubhubbub,
            copyright=self.copyright,
            authors=[self.author],
            skip_days=self.skip_days,
            skip_hours=self.skip_hours,
            web_master=self.web_master,
            feed_url=self.feed_url,
            explicit=self.explicit,
            image=self.image,
            owner=self.owner,
            complete=self.complete,
            new_feed_url=self.new_feed_url,
            xslt=self.xslt,
        )
        # Test that the fields are actually set
        self.test_baseFeed()

    def test_constructorUnknownAttributes(self):
        self.assertRaises(TypeError, Podcast, naem="Oh, looks like a typo")
        self.assertRaises(TypeError, Podcast, "Haha, No Keyword")

    def test_baseFeed(self):
        fg = self.fg

        assert fg.name == self.name

        assert fg.authors[0] == self.author
        assert fg.web_master == self.web_master

        assert fg.website == self.website

        assert fg.description == self.description
        assert fg.subtitle == self.subtitle

        assert fg.language == self.language
        assert fg.feed_url == self.feed_url
        assert fg.image == self.image
        assert fg.owner == self.owner
        assert fg.complete == self.complete
        assert fg.pubsubhubbub == self.pubsubhubbub
        assert fg.cloud == (self.cloudDomain, self.cloudPort, self.cloudPath,
                            self.cloudRegisterProcedure, self.cloudProtocol)
        assert fg.copyright == self.copyright
        assert fg.new_feed_url == self.new_feed_url
        assert fg.skip_days == self.skip_days
        assert fg.skip_hours == self.skip_hours
        assert fg.xslt == self.xslt

    def test_rssFeedFile(self):
        fg = self.fg
        rssString = self.getRssFeedFileContents(fg, xml_declaration=False)\
            .replace('\n', '')
        self.checkRssString(rssString)

    def getRssFeedFileContents(self, fg, **kwargs):
        # Keep track of our temporary file and its filename
        filename = None
        file = None
        try:
            # Get our temporary file name
            file = tempfile.NamedTemporaryFile(delete=False)
            filename = file.name
            # Close the file; we will just use its name
            file.close()
            # Write the RSS to the file (overwriting it)
            fg.rss_file(filename=filename, **kwargs)
            # Read the resulting RSS
            with open(filename, "r") as myfile:
                rssString = myfile.read()
        finally:
            # We don't need the file any longer, so delete it
            if filename:
                os.unlink(filename)
            elif file:
                # Ops, we were interrupted between the first and second stmt
                filename = file.name
                file.close()
                os.unlink(filename)
            else:
                # We were interrupted between entering the try-block and
                # getting the temporary file. Not much we can do.
                pass
        return rssString


    def test_rssFeedString(self):
        fg = self.fg
        rssString = fg.rss_str(xml_declaration=False)
        self.checkRssString(rssString)

    def test_rssStringAndFileAreEqual(self):
        rss_string = self.fg.rss_str()
        rss_file = self.getRssFeedFileContents(self.fg)
        self.assertEqual(rss_string, rss_file)

    def checkRssString(self, rssString):
        feed = etree.fromstring(rssString)
        nsRss = self.nsContent
        nsAtom = "http://www.w3.org/2005/Atom"

        channel = feed.find("channel")
        assert channel != None

        assert channel.find("title").text == self.name
        assert channel.find("description").text == self.description
        assert channel.find("{%s}subtitle" % self.nsItunes).text == \
            self.subtitle
        assert channel.find("link").text == self.website
        assert channel.find("lastBuildDate").text != None
        assert channel.find("language").text == self.language
        assert channel.find("docs").text == "http://www.rssboard.org/rss-specification"
        assert self.programname in channel.find("generator").text
        assert channel.find("cloud").get('domain') == self.cloudDomain
        assert channel.find("cloud").get('port') == self.cloudPort
        assert channel.find("cloud").get('path') == self.cloudPath
        assert channel.find("cloud").get('registerProcedure') == self.cloudRegisterProcedure
        assert channel.find("cloud").get('protocol') == self.cloudProtocol
        assert channel.find("copyright").text == self.copyright
        assert channel.find("docs").text == self.docs
        assert self.author.email in channel.find("managingEditor").text
        assert channel.find("skipDays").find("day").text in self.skip_days
        assert int(channel.find("skipHours").find("hour").text) in self.skip_hours
        assert self.web_master.email in channel.find("webMaster").text

        links = channel.findall("{%s}link" % nsAtom)
        selflinks = [link for link in links if link.get('rel') == 'self']
        hublinks = [link for link in links if link.get('rel') == 'hub']

        assert selflinks, "No <atom:link rel='self'> element found"
        selflink = selflinks[0]
        assert selflink.get('href') == self.feed_url
        assert selflink.get('type') == 'application/rss+xml'

        assert hublinks, "No <atom:link rel='hub'> element found"
        hublink = hublinks[0]
        assert hublink.get('href') == self.pubsubhubbub
        assert hublink.get('type') is None

        assert channel.find("{%s}image" % self.nsItunes).get('href') == \
            self.image
        owner = channel.find("{%s}owner" % self.nsItunes)
        assert owner.find("{%s}name" % self.nsItunes).text == self.owner.name
        assert owner.find("{%s}email" % self.nsItunes).text == self.owner.email
        assert channel.find("{%s}complete" % self.nsItunes).text.lower() == \
            "yes"
        assert channel.find("{%s}new-feed-url" % self.nsItunes).text == \
            self.new_feed_url

    def test_feedUrlValidation(self):
        self.assertRaises(ValueError, setattr, self.fg, "feed_url",
                          "example.com/feed.rss")

    def test_generator(self):
        software_name = "My Awesome Software"
        software_version = (1, 0)
        software_url = "http://example.com/awesomesoft/"

        # Using set_generator, text includes python-podgen
        self.fg.set_generator(software_name)
        rss = self.fg._create_rss()
        generator = rss.find("channel").find("generator").text
        assert software_name in generator
        assert self.programname in generator

        # Using set_generator, text excludes python-podgen
        self.fg.set_generator(software_name, exclude_podgen=True)
        generator = self.fg._create_rss().find("channel").find("generator").text
        assert software_name in generator
        assert self.programname not in generator

        # Using set_generator, text includes name, version and url
        self.fg.set_generator(software_name, software_version, software_url)
        generator = self.fg._create_rss().find("channel").find("generator").text
        assert software_name in generator
        assert str(software_version[0]) in generator
        assert str(software_version[1]) in generator
        assert software_url in generator

        # Using generator directly, text excludes python-podgen
        self.fg.generator = software_name
        generator = self.fg._create_rss().find("channel").find("generator").text
        assert software_name in generator
        assert self.programname not in generator

    def test_str(self):
        assert str(self.fg) == self.fg.rss_str(
            minimize=False,
            encoding="UTF-8",
            xml_declaration=True
        )

    def test_updated(self):
        date = datetime.datetime(2016, 1, 1, 0, 10, tzinfo=dateutil.tz.tzutc())

        def getLastBuildDateElement(fg):
            return fg._create_rss().find("channel").find("lastBuildDate")

        # Test that it has a default
        assert getLastBuildDateElement(self.fg) is not None

        # Test that it respects my custom value
        self.fg.last_updated = date
        lastBuildDate = getLastBuildDateElement(self.fg)
        assert lastBuildDate is not None
        assert dateutil.parser.parse(lastBuildDate.text) == date

        # Test that it is left out when set to False
        self.fg.last_updated = False
        lastBuildDate = getLastBuildDateElement(self.fg)
        assert lastBuildDate is None

    def test_AuthorEmail(self):
        # Just email - so use managingEditor, not dc:creator or itunes:author
        # This is per the RSS best practices, see the section about dc:creator
        self.fg.authors = [Person(None, "*****@*****.**")]
        channel = self.fg._create_rss().find("channel")
        # managingEditor uses email?
        assert channel.find("managingEditor").text == self.fg.authors[0].email
        # No dc:creator?
        assert channel.find("{%s}creator" % self.nsDc) is None
        # No itunes:author?
        assert channel.find("{%s}author" % self.nsItunes) is None

    def test_AuthorName(self):
        # Just name - use dc:creator and itunes:author, not managingEditor
        self.fg.authors = [Person("Just a. Name")]
        channel = self.fg._create_rss().find("channel")
        # No managingEditor?
        assert channel.find("managingEditor") is None
        # dc:creator equals name?
        assert channel.find("{%s}creator" % self.nsDc).text == \
               self.fg.authors[0].name
        # itunes:author equals name?
        assert channel.find("{%s}author" % self.nsItunes).text == \
            self.fg.authors[0].name

    def test_AuthorNameAndEmail(self):
        # Both name and email - use managingEditor and itunes:author,
        # not dc:creator
        self.fg.authors = [Person("Both a name", "*****@*****.**")]
        channel = self.fg._create_rss().find("channel")
        # Does managingEditor follow the pattern "email (name)"?
        self.assertEqual(self.fg.authors[0].email +
                         " (" + self.fg.authors[0].name + ")",
                         channel.find("managingEditor").text)
        # No dc:creator?
        assert channel.find("{%s}creator" % self.nsDc) is None
        # itunes:author uses name only?
        assert channel.find("{%s}author" % self.nsItunes).text == \
            self.fg.authors[0].name

    def test_multipleAuthors(self):
        # Multiple authors - use itunes:author and dc:creator, not
        # managingEditor.

        person1 = Person("Multiple", "*****@*****.**")
        person2 = Person("Are", "*****@*****.**")
        self.fg.authors = [person1, person2]
        channel = self.fg._create_rss().find("channel")

        # Test dc:creator
        author_elements = \
            channel.findall("{%s}creator" % self.nsDc)
        author_texts = [e.text for e in author_elements]

        assert len(author_texts) == 2
        assert person1.name in author_texts[0]
        assert person1.email in author_texts[0]
        assert person2.name in author_texts[1]
        assert person2.email in author_texts[1]

        # Test itunes:author
        itunes_author = channel.find("{%s}author" % self.nsItunes)
        assert itunes_author is not None
        itunes_author_text = itunes_author.text
        assert person1.name in itunes_author_text
        assert person1.email not in itunes_author_text
        assert person2.name in itunes_author_text
        assert person2.email not in itunes_author_text

        # Test that managingEditor is not used
        assert channel.find("managingEditor") is None

    def test_authorsInvalidValue(self):
        self.assertRaises(TypeError, self.do_authorsInvalidValue)

    def do_authorsInvalidValue(self):
        self.fg.authors = Person("Opsie", "*****@*****.**")


    def test_webMaster(self):
        self.fg.web_master = Person(None, "*****@*****.**")
        channel = self.fg._create_rss().find("channel")
        assert channel.find("webMaster").text == self.fg.web_master.email

        self.assertRaises(ValueError, setattr, self.fg, "web_master",
                          Person("Mr. No Email Address"))

        self.fg.web_master = Person("Both a name", "*****@*****.**")
        channel = self.fg._create_rss().find("channel")
        # Does webMaster follow the pattern "email (name)"?
        self.assertEqual(self.fg.web_master.email +
                         " (" + self.fg.web_master.name + ")",
                         channel.find("webMaster").text)

    def test_categoryWithoutSubcategory(self):
        c = Category("Arts")
        self.fg.category = c
        channel = self.fg._create_rss().find("channel")
        itunes_category = channel.find("{%s}category" % self.nsItunes)
        assert itunes_category is not None

        self.assertEqual(itunes_category.get("text"), c.category)

        assert itunes_category.find("{%s}category" % self.nsItunes) is None

    def test_categoryWithSubcategory(self):
        c = Category("Arts", "Food")
        self.fg.category = c
        channel = self.fg._create_rss().find("channel")
        itunes_category = channel.find("{%s}category" % self.nsItunes)
        assert itunes_category is not None
        itunes_subcategory = itunes_category\
            .find("{%s}category" % self.nsItunes)
        assert itunes_subcategory is not None
        self.assertEqual(itunes_subcategory.get("text"), c.subcategory)

    def test_categoryChecks(self):
        c = ("Arts", "Food")
        self.assertRaises(TypeError, setattr, self.fg, "category", c)

    def test_explicitIsExplicit(self):
        self.fg.explicit = True
        channel = self.fg._create_rss().find("channel")
        itunes_explicit = channel.find("{%s}explicit" % self.nsItunes)
        assert itunes_explicit is not None
        assert itunes_explicit.text.lower() in ("yes", "explicit", "true"),\
            "itunes:explicit was %s, expected yes, explicit or true" \
            % itunes_explicit.text

    def test_explicitIsClean(self):
        self.fg.explicit = False
        channel = self.fg._create_rss().find("channel")
        itunes_explicit = channel.find("{%s}explicit" % self.nsItunes)
        assert itunes_explicit is not None
        assert itunes_explicit.text.lower() in ("no", "clean", "false"),\
            "itunes:explicit was %s, expected no, clean or false" \
            % itunes_explicit.text

    def test_mandatoryValues(self):
        # Try to create a Podcast once for each mandatory property.
        # On each iteration, exactly one of the properties is not set.
        # Therefore, an exception should be thrown on each iteration.
        mandatory_properties = set([
            "description",
            "title",
            "link",
            "explicit",
        ])

        for test_property in mandatory_properties:
            fg = Podcast()
            if test_property != "description":
                fg.description = self.description
            if test_property != "title":
                fg.name = self.name
            if test_property != "link":
                fg.website = self.website
            if test_property != "explicit":
                fg.explicit = self.explicit
            try:
                self.assertRaises(ValueError, fg._create_rss)
            except AssertionError as e:
                raise_from(AssertionError(
                    "The test failed for %s" % test_property), e)

    def test_withholdFromItunesOffByDefault(self):
        assert not self.fg.withhold_from_itunes

    def test_withholdFromItunes(self):
        self.fg.withhold_from_itunes = True
        itunes_block = self.fg._create_rss().find("channel")\
            .find("{%s}block" % self.nsItunes)
        assert itunes_block is not None
        self.assertEqual(itunes_block.text.lower(), "yes")

        self.fg.withhold_from_itunes = False
        itunes_block = self.fg._create_rss().find("channel")\
            .find("{%s}block" % self.nsItunes)
        assert itunes_block is None

    def test_modifyingSkipDaysAfterwards(self):
        self.fg.skip_days.add("Unrecognized day")
        self.assertRaises(ValueError, self.fg.rss_str)
        self.fg.skip_days.remove("Unrecognized day")
        self.fg.rss_str()  # Now it works

    def test_modifyingSkipHoursAfterwards(self):
        self.fg.skip_hours.add(26)
        self.assertRaises(ValueError, self.fg.rss_str)
        self.fg.skip_hours.remove(26)
        self.fg.rss_str()  # Now it works

    # Tests for xslt
    def test_xslt_str(self):
        def use_str(**kwargs):
            return self.fg.rss_str(**kwargs)
        self.help_test_xslt_using(use_str)

    def test_xslt_file(self):
        def use_file(**kwargs):
            return self.getRssFeedFileContents(self.fg, **kwargs)
        self.help_test_xslt_using(use_file)

    def help_test_xslt_using(self, generated_feed):
        """Run tests for xslt, generating the feed str using the given function.
        """
        xslt_path = "http://example.com/mystylesheet.xsl"
        xslt_pi = "<?xml-stylesheet"

        # No xslt when set to None
        self.fg.xslt = None
        assert xslt_pi not in generated_feed()
        assert xslt_pi not in generated_feed(minimize=True)
        assert xslt_pi not in generated_feed(xml_declaration=False)

        self.fg.xslt = xslt_path

        # Now we have the stylesheet in there
        assert xslt_pi in generated_feed()
        assert xslt_pi in generated_feed(minimize=True)
        assert xslt_pi in generated_feed(xml_declaration=False)

        assert xslt_path in generated_feed()
        assert xslt_path in generated_feed(minimize=True)
        assert xslt_path in generated_feed(xml_declaration=False)

    def test_imageWarningNoExt(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            self.assertEqual(len(w), 0)

            # Set image to a URL without proper file extension
            no_ext = "http://static.example.com/images/logo"
            self.fg.image = no_ext
            # Did we get a warning?
            self.assertEqual(1, len(w))
            assert issubclass(w.pop().category, NotSupportedByItunesWarning)
            # Was the image set?
            self.assertEqual(no_ext, self.fg.image)

    def test_imageWarningBadExt(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")

            # Set image to a URL with an unsupported file extension
            bad_ext = "http://static.example.com/images/logo.gif"
            self.fg.image = bad_ext
            # Did we get a warning?
            self.assertEqual(1, len(w))
            # Was it of the correct type?
            assert issubclass(w.pop().category, NotSupportedByItunesWarning)
            # Was the image still set?
            self.assertEqual(bad_ext, self.fg.image)

    def test_imageNoWarningWithGoodExt(self):
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")

            # Set image to a URL with a supported file extension
            extensions = ["jpg", "png", "jpeg"]
            for extension in extensions:
                good_ext = "http://static.example.com/images/logo." + extension
                self.fg.image = good_ext
                # Did we get no warning?
                self.assertEqual(0, len(w), "Extension %s raised warnings (%s)"
                                 % (extension, w))
                # Was the image set?
                self.assertEqual(good_ext, self.fg.image)
Beispiel #11
0
def create_rss(type, download):
    """Create an example podcast and print it or save it to a file."""
    
    # Create the Podcast & initialize the feed
    default_channel = Channel.defaultChannel()
    
    p = Podcast()
    p.name          = default_channel.name
    p.description   = default_channel.description
    p.website       = default_channel.website
    p.explicit      = default_channel.explicit
    p.image         = default_channel.image

    p.copyright     = default_channel.copyright
    p.language      = default_channel.language
    p.feed_url      = default_channel.feed_url
    p.category      = Category(default_channel.category)
    # p.category = Category('Technology', 'Podcasting')
    # p.xslt      = "https://example.com/feed/stylesheet.xsl"  # URL of XSLT stylesheet

    p.authors   = [Person(default_channel.authors, default_channel.authors_email)]
    p.owner     = Person(default_channel.owner, default_channel.owner_email)

    # Other Attributes
    p.generator = " "
    
    # Others for iTunes
    # p.complete = False
    # p.new_feed_url = 'http://example.com/new-feed.rss'

    # e1 = p.add_episode()
    # e1.id = 'http://lernfunk.de/_MEDIAID_123#1'
    # e1.title = 'First Element'
    # e1.summary = htmlencode('''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Tamen
    #         aberramus a proposito, et, ne longius, prorsus, inquam, Piso, si ista
    #         mala sunt, placet. Aut etiam, ut vestitum, sic sententiam habeas aliam
    #         domesticam, aliam forensem, ut in fronte ostentatio sit, intus veritas
    #         occultetur? Cum id fugiunt, re eadem defendunt, quae Peripatetici,
    #         verba <3.''')
    # e1.link = 'http://example.com'
    # e1.authors = [Person('Lars Kiesow', '*****@*****.**')]
    # e1.publication_date = datetime.datetime(2014, 5, 17, 13, 37, 10, tzinfo=pytz.utc)
    # # e1.media = Media("http://example.com/episodes/loremipsum.mp3", 454599964,
    # #                  duration=
    # #                  datetime.timedelta(hours=1, minutes=32, seconds=19))
    # e1.media = Media("http://example.com/episodes/loremipsum.mp3", 454599964)

    # Add some episodes
    p.episodes += [
       Episode(title = download.title, 
            subtitle = download.subtitle,
            # id=str(uuid.uuid4()),
            position =2,
            media = Media(download.media_url, size=download.media_size, duration=timedelta(seconds=download.media_duration)),
            image = download.image_url,
            publication_date = datetime(year=2021, month=1, day=8, hour=10, minute=0, tzinfo=pytz.utc),
            summary = download.summary)
    ,
       Episode(title="Episode 2 - The Crazy Ones",
            subtitle="this is a cool episode, this is for th crazy ones",
            position=1,
            image="https://github.com/oliverbarreto/PersonalPodcast/raw/main/site-logo-1400x1400.png",
            media=Media("https://github.com/oliverbarreto/PersonalPodcast/raw/main/downloaded_with_pytube_Apple%20Steve%20Jobs%20Heres%20To%20The%20Crazy%20Ones.mp4", type="audio/mpeg", size=989, duration=timedelta(hours=0, minutes=1, seconds=1)),
            publication_date = datetime(year=2021, month=1, day=6, hour=10, minute=0, tzinfo=pytz.utc),
            summary=htmlencode("wow wow wow summary"))
    ,
        Episode(title="Episode 3 - The Super Crazy",
            subtitle="crazy ones revisited",
            position=0,
            image="https://github.com/oliverbarreto/PersonalPodcast/raw/main/site-logo-1400x1400.png",
            media=Media("https://drive.google.com/file/d/1X5Mwa8V0Su1IDqhcQL7LdzEY0VaMC1Nn", type="audio/mpeg", size=989, duration=timedelta(hours=0, minutes=1, seconds=1)),
            publication_date = datetime(year=2021, month=1, day=10, hour=10, minute=0, tzinfo=pytz.utc),
            summary=download.summary)
    ]

    # Should we just print out, or write to file?
    if type == 'print':
        # Print
        print_enc(p.rss_str())
    elif type== 'feed.xml':
        # Write to file
        p.rss_file(type, minimize=False)
        print("\n")
        print("feed.xml created !!!")