def test_perspective_primary_draw_metadata(display):
    perspective = get_primary_perspective(display)

    feed = Feed(url="feed url",
                title="feed title",
                description="feed description",
                link="feed link",
                last_build_date="feed last_build_date",
                copyright="feed copyright",
                episodes=[])
    episode = Episode(feed,
                      title="episode title",
                      description="episode description",
                      link="episode link",
                      pubdate="episode pubdate",
                      copyright="episode copyright",
                      enclosure="episode enclosure")
    feed.episodes.append(episode)
    display.feeds["feed url"] = feed
    perspective._draw_metadata(perspective._metadata_window, feed=feed)
    perspective._draw_metadata(perspective._metadata_window, episode=episode)
Esempio n. 2
0
def test_perspective_primary_draw_metadata(display):
    perspective = get_primary_perspective(display)

    feed = Feed(url="feed url",
                title="feed title",
                description="feed description",
                link="feed link",
                last_build_date="feed last_build_date",
                copyright="feed copyright",
                episodes=[])
    episode = Episode(feed,
                      title="episode title",
                      description="episode description",
                      link="episode link",
                      pubdate="episode pubdate",
                      copyright="episode copyright",
                      enclosure="episode enclosure")
    display.database.replace_feed(feed)
    display.database.replace_episode(feed, episode)
    perspective._draw_metadata(perspective._metadata_window)
    perspective._draw_metadata(perspective._metadata_window)
Esempio n. 3
0
def test_display_draw_metadata(display):
    feed = Feed(url="feed url",
                title="feed title",
                description="feed description",
                link="feed link",
                last_build_date="feed last_build_date",
                copyright="feed copyright",
                episodes=[])
    episode = Episode(feed,
                      title="episode title",
                      description="episode description",
                      link="episode link",
                      pubdate="episode pubdate",
                      copyright="episode copyright",
                      enclosure="episode enclosure")
    feed.episodes.append(episode)
    display._feeds["feed url"] = feed
    display._active_window = 0
    display._draw_metadata()
    display._active_window = 1
    display._draw_metadata()
Esempio n. 4
0
def test_display_display_footer_empty(display):
    display.display()
    display._footer_window.attron.assert_called_with(curses.A_BOLD)
    display._footer_window.addstr.assert_called_with(
        1, 0, "No feeds added -- Press h for help"
    )
    display._stdscr.reset_mock()
    feed = Feed(url="feed url",
                title="feed title",
                description="feed description",
                link="feed link",
                last_build_date="feed last_build_date",
                copyright="feed copyright",
                episodes=[])
    display._feeds["feed url"] = feed
    display.display()
    display._footer_window.attron.assert_called_with(curses.A_BOLD)
    display._footer_window.addstr.assert_called_with(
        1, 0,
        "Processed 1 feeds with 0 total episodes (avg. 0 episodes, med. 0)"
        " -- Press h for help"
    )
Esempio n. 5
0
def test_perspective_primary_queue_unplayed(display):
    perspective = get_primary_perspective(display)

    feed = Feed(url="feed url",
                title="feed title",
                description="feed description",
                link="feed link",
                last_build_date="feed last_build_date",
                copyright="feed copyright",
                episodes=[])
    episode1 = Episode(feed,
                       title="episode1 title",
                       description="episode1 description",
                       link="episode1 link",
                       pubdate="episode1 pubdate",
                       copyright="episode1 copyright",
                       enclosure="episode1 enclosure",
                       played=True)
    episode2 = Episode(feed,
                       title="episode2 title",
                       description="episode2 description",
                       link="episode2 link",
                       pubdate="episode2 pubdate",
                       copyright="episode2 copyright",
                       enclosure="episode2 enclosure")
    display.display()
    display.database.replace_feed(feed)
    display.database.replace_episodes(feed, [episode1, episode2])
    perspective._feed_menu.update_items(None)
    perspective._episode_menu.update_items(feed)
    perspective._active_window = 0
    perspective._queue_unplayed_feed_episodes = False
    perspective._create_player_from_selected()
    assert display.queue.length == 2
    display.queue.clear()
    perspective._queue_unplayed_feed_episodes = True
    perspective._create_player_from_selected()
    assert display.queue.length == 1
Esempio n. 6
0
    def feeds(self) -> List[Feed]:
        """Retrieve the list of Feeds.

        Returns:
            List[Feed]: all Feed's in the database
        """
        cursor = self._conn.cursor()
        cursor.execute(self.SQL_FEEDS_ALL)

        feeds = []
        for row in cursor.fetchall():
            feed = Feed(url=row[0] if row[0].startswith('http') else None,
                        file=row[0] if not row[0].startswith('http') else None,
                        title=row[1],
                        description=row[2],
                        link=row[3],
                        last_build_date=row[4],
                        copyright=row[5])

            if feed.title:
                feeds.append(feed)
            else:
                self.delete_feed(feed)
        return feeds
Esempio n. 7
0
    def feed(self, key) -> Feed:
        """Retrieve a feed by key.

        :param key the key of the Feed to retrieve, which is the feed's primary
          key in the database
        :returns Feed: the matching Feed, if it exists, or None
        """
        cursor = self._conn.cursor()
        cursor.execute(self.SQL_FEED_BY_KEY, (key, ))

        result = cursor.fetchone()
        if result is None:
            return None
        else:
            return Feed(
                url=result[0] if result[0].startswith("http") else None,
                file=result[0] if not result[0].startswith("http") else None,
                title=result[1],
                description=result[2],
                link=result[3],
                last_build_date=result[4],
                copyright=result[5],
                episodes=[],
            )
Esempio n. 8
0
def test_display_execute_command(display):
    fname = "test_display_execute_command_output.mp3"
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed,
                        title="episode title",
                        description="episode description",
                        link="episode link",
                        pubdate="episode pubdate",
                        copyright="episode copyright",
                        enclosure=fname)
    castero.config.Config.data = {'execute_command': 'touch {file}'}
    if os.path.exists(fname):
        os.remove(fname)
    display.execute_command(myepisode)

    successful = False
    for i in range(10000):
        if os.path.exists(fname):
            successful = True
            break

    if successful:
        os.remove(fname)
    assert successful
Esempio n. 9
0
def test_episode_missing_property_pubdate():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, title=title)
    assert myepisode.pubdate == "Publish date not available."
Esempio n. 10
0
def test_episode_missing_property_description():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, title=title)
    assert myepisode.description == "Description not available."
Esempio n. 11
0
def test_episode_str_description():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, description=description)
    assert str(myepisode) == description
Esempio n. 12
0
def test_episode_only_description():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, description=description)
    assert isinstance(myepisode, Episode)
Esempio n. 13
0
def test_episode_playable_remote():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    episode = myfeed.parse_episodes()[0]
    playable = episode.get_playable()
    assert not episode.downloaded
    assert playable == "http://example.com/myfeed_item1_title.mp3"
Esempio n. 14
0
def test_display_nonempty(display):
    myfeed = Feed(file=my_dir + "/feeds/valid_enclosures.xml")
    display._feeds[myfeed._file] = myfeed
    display.create_menus()
    display.display()
Esempio n. 15
0
    def reload(self, display=None, feeds=None) -> None:
        """Reload feeds in the database.

        To preserve user metadata for episodes (such as played/marked status),
        we use Episode.replace_from() which "manually" copies such fields to
        the new downloaded episode. This is necessary because downloaded
        episodes are new Episode instances and we can't guarantee they have any
        of the same properties.

        Therefore, Episode.replace_from() _must_ be updated if any new user
        metadata fields are added.

        Also: to determine which episodes are the same in order to copy user
        metadata, we simply check whether the string representation of the two
        episodes are matching (usually the episodes' titles). This could cause
        issues if a feed has multiple episodes with the same title, although it
        does not require episode titles to be globally unique (that is,
        episodes with the same name in different feeds will never have issues).

        This method adheres to the max_episodes config parameter to limit the
        number of episodes saved per feed.

        Args:
            display: (optional) the display to write status updates to
            feeds: (optional) a list of feeds to reload. If not specified,
                all feeds in the database will be reloaded
        """
        if feeds is None:
            feeds = self.feeds()
        total_feeds = len(feeds)
        completed_feeds = 0

        reqs = []
        url_pairs = {}
        file_feeds = []
        # Create async requests for each URL feed. We also keep a map from
        # each feed's URL to the Feed object itself in order to access the
        # object when a request completes (since the response object is all
        # that we are given).
        # We also keep track of file-based feeds, which are handled afterwards.
        for feed in feeds:
            if feed.key.startswith("http"):
                url_pairs[feed.key] = feed
                reqs.append(Net.GGet(feed.key))
            else:
                file_feeds.append(feed)

        # handle each response as downloads complete asynchronously
        for response in grequests.imap(reqs, size=3):
            if display is not None:
                display.change_status("Reloading feeds (%d/%d)" %
                                      (completed_feeds, total_feeds))
            old_feed = url_pairs[response.request.url]
            new_feed = Feed(url=response.request.url, response=response)
            self._reload_feed_data(old_feed, new_feed)

            completed_feeds += 1

        # handle each file-based feed
        for old_feed in file_feeds:
            new_feed = Feed(file=old_feed.key)
            self._reload_feed_data(old_feed, new_feed)

            completed_feeds += 1
            if display is not None:
                display.change_status("Reloading feeds (%d/%d)" %
                                      (completed_feeds, total_feeds))

        if display is not None:
            display.change_status("Successfully reloaded %d feeds" %
                                  total_feeds)
            display.menus_valid = False
Esempio n. 16
0
def test_episode_missing_property_enclosure():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, title=title)
    assert myepisode.enclosure == "Enclosure not available."
Esempio n. 17
0
def test_episode_metadata_with_progress_no_error():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    episode = myfeed.parse_episodes()[0]
    episode._progress = 1000
    assert isinstance(episode.metadata, str)
Esempio n. 18
0
def test_episode_only_title():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, title=title)
    assert isinstance(myepisode, Episode)
Esempio n. 19
0
def test_episode_progress():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    episode = myfeed.parse_episodes()[0]
    episode._progress = 1000
    assert episode.progress == 1000
Esempio n. 20
0
def test_episode_str_title():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, title=title)
    assert str(myepisode) == title
Esempio n. 21
0
    def reload(self, display=None) -> None:
        """Reload all feeds in the database.

        To preserve user metadata for episodes (such as played/marked status),
        we use Episode.replace_from() which "manually" copies such fields to
        the new downloaded episode. This is necessary because downloaded
        episodes are new Episode instances and we can't guarantee they have any
        of the same properties.

        Therefore, Episode.replace_from() _must_ be updated if any new user
        metadata fields are added.

        Also: to determine which episodes are the same in order to copy user
        metadata, we simply check whether the string representation of the two
        episodes are matching (usually the episodes' titles). This could cause
        issues if a feed has multiple episodes with the same title, although it
        does not require episode titles to be globally unique (that is,
        episodes with the same name in different feeds will never have issues).

        This method adheres to the max_episodes config parameter to limit the
        number of episodes saved per feed.

        Args:
            display: (optional) the display to write status updates to
        """
        feeds = self.feeds()
        total_feeds = len(feeds)
        current_feed = 1

        for feed in self.feeds():
            if display is not None:
                display.change_status("Reloading feeds (%d/%d)" %
                                      (current_feed, total_feeds))

            # assume urls have http in them
            if "http" in feed.key:
                new_feed = Feed(url=feed.key)
            else:
                new_feed = Feed(file=feed.key)

            # keep user metadata for episodes intact
            new_episodes = new_feed.parse_episodes()
            old_episodes = self.episodes(feed)
            for new_ep in new_episodes:
                matching_olds = [
                    old_ep for old_ep in old_episodes
                    if str(old_ep) == str(new_ep)
                ]
                if len(matching_olds) == 1:
                    new_ep.replace_from(matching_olds[0])

            # limit number of episodes, if necessary
            max_episodes = int(Config["max_episodes"])
            if max_episodes != -1:
                new_episodes = new_episodes[:max_episodes]

            self.replace_feed(new_feed)
            self.replace_episodes(new_feed, new_episodes)

        if display is not None:
            display.change_status("Feeds successfully reloaded")
            display.menus_valid = False
Esempio n. 22
0
def test_episode_missing_property_title():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, description=description)
    assert myepisode.title == "Title not available."
Esempio n. 23
0
import os
from unittest import mock

from castero.downloadqueue import DownloadQueue
from castero.episode import Episode
from castero.feed import Feed

my_dir = os.path.dirname(os.path.realpath(__file__))

feed = Feed(file=my_dir + "/feeds/valid_enclosures.xml")
episode1 = Episode(feed=feed, title="episode1 title")
episode2 = Episode(feed=feed, title="episode2 title")


def test_downloadqueue_init():
    mydownloadqueue = DownloadQueue()
    assert isinstance(mydownloadqueue, DownloadQueue)


def test_downloadqueue_add():
    mydownloadqueue = DownloadQueue()
    assert mydownloadqueue.length == 0
    mydownloadqueue.add(episode1)
    assert mydownloadqueue.length == 1
    mydownloadqueue.add(episode1)
    assert mydownloadqueue.length == 1
    mydownloadqueue.add(episode2)
    assert mydownloadqueue.length == 2


def test_downloadqueue_start():
Esempio n. 24
0
def test_episode_missing_property_link():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, title=title)
    assert myepisode.link == "Link not available."
Esempio n. 25
0
def test_display_nonempty(display):
    myfeed = Feed(file=my_dir + "/feeds/valid_enclosures.xml")
    display.database.feeds = mock.MagicMock(return_value=[myfeed])
    display.menus_valid = False
    display.display()
Esempio n. 26
0
def test_episode_missing_property_copyright():
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = Episode(myfeed, title=title)
    assert myepisode.copyright == "No copyright specified."
Esempio n. 27
0
import os
from unittest import mock


from castero.config import Config
from castero.feed import Feed
from castero.queue import Queue
from castero.player import Player

my_dir = os.path.dirname(os.path.realpath(__file__))

feed = Feed(file=my_dir + "/feeds/valid_basic.xml")


def test_queue_init(display):
    myqueue = Queue(display)
    assert isinstance(myqueue, Queue)


def test_queue_first(display):
    myqueue = Queue(display)
    player1 = mock.MagicMock(spec=Player)

    myqueue.add(player1)
    assert myqueue.first == player1


def test_queue_get(display):
    myqueue = Queue(display)
    player1 = mock.MagicMock(spec=Player)
    player2 = mock.MagicMock(spec=Player)