Beispiel #1
0
 def add_feed(self) -> None:
     """Prompt the user for a feed and add it, if possible.
     """
     path = self._get_input_str("Enter the URL or path of the feed: ")
     try:
         # assume urls have http in them
         if "http" in path:
             feed = Feed(url=path)
         else:
             feed = Feed(file=path)
         if feed.validated:
             self.database.replace_feed(feed)
             self.database.replace_episodes(feed, feed.parse_episodes())
         self.menus_valid = False
         self.change_status("Feed '%s\' successfully added" % str(feed))
     except FeedError as e:
         if isinstance(e, FeedLoadError):
             self.change_status("FeedLoadError: %s" % str(e))
         elif isinstance(e, FeedDownloadError):
             self.change_status("FeedDownloadError: %s" % str(e))
         elif isinstance(e, FeedParseError):
             self.change_status("FeedParseError: %s" % str(e))
         elif isinstance(e, FeedStructureError):
             self.change_status("FeedStructureError: %s" % str(e))
         else:
             self.change_status("FeedError [ambiguous]: %s" % str(e))
Beispiel #2
0
    def _reload_feed_data(self, old_feed: Feed, new_feed: Feed):
        """Helper method to update a feed and its episodes in the database.

        Args:
            old_feed: the original Feed to be replaced
            new_feed: a Feed with new/updated data
        """
        # keep user metadata for episodes intact
        new_episodes = new_feed.parse_episodes()
        old_episodes = self.episodes(new_feed)
        episode_progresses = {}
        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])
                if (matching_olds[0].progress != 0):
                    episode_progresses[str(new_ep)] = new_ep.progress

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

        # update the feed and its episodes in the database
        self.replace_feed(new_feed)
        self.replace_episodes(new_feed, new_episodes)

        # ensure episodes have their progress carried over, if necessary
        added_episodes = self.episodes(new_feed)
        for episode in added_episodes:
            if str(episode) in episode_progresses:
                self.replace_progress(episode, episode.progress)
Beispiel #3
0
    def _reload_feed_data(self, old_feed: Feed, new_feed: Feed):
        """Helper method to update a feed and its episodes in the database.

        Args:
            old_feed: the original Feed to be replaced
            new_feed: a Feed with new/updated data
        """
        # keep user metadata for episodes intact
        new_episodes = new_feed.parse_episodes()
        old_episodes = self.episodes(new_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]

        # update the feed and its episodes in the database
        self.replace_feed(new_feed)
        self.replace_episodes(new_feed, new_episodes)
Beispiel #4
0
def test_episode_playable_local():
    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(my_dir, "downloaded")
    myfeed = Feed(file=my_dir + "/feeds/valid_enclosures.xml")
    episode = myfeed.parse_episodes()[0]
    playable = episode.get_playable()
    assert episode.downloaded
    assert playable == os.path.join(DataFile.DEFAULT_DOWNLOADED_DIR,
                                    "myfeed_title", "myfeed_item1_title.mp3")

    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(DataFile.DATA_DIR,
                                                   "downloaded")
Beispiel #5
0
def test_episode_download():
    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(my_dir, "downloaded")
    mydownloadqueue = DownloadQueue()
    myfeed = Feed(file=my_dir + "/feeds/valid_enclosures.xml")
    myepisode = myfeed.parse_episodes()[1]
    DataFile.download_to_file = mock.MagicMock(name="download_to_file")
    myepisode.download(mydownloadqueue)
    assert DataFile.download_to_file.call_count == 1

    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(DataFile.DATA_DIR,
                                                   "downloaded")
Beispiel #6
0
def test_database_add_episodes(prevent_modification):
    copyfile(my_dir + "/datafiles/database_example1.db", Database.PATH)
    mydatabase = Database()

    myfeed_path = my_dir + "/feeds/valid_basic.xml"
    myfeed = Feed(file=myfeed_path)
    episodes = myfeed.parse_episodes()

    mydatabase.replace_feed(myfeed)
    assert len(mydatabase.episodes(myfeed)) == 0
    mydatabase.replace_episodes(myfeed, episodes)
    assert len(mydatabase.episodes(myfeed)) == len(episodes)
Beispiel #7
0
def test_episode_download_with_display_no_enclosure(display):
    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(my_dir, "downloaded")
    mydownloadqueue = DownloadQueue()
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = myfeed.parse_episodes()[1]
    DataFile.download_to_file = mock.MagicMock(name="download_to_file")
    display.change_status = mock.MagicMock(name="change_status")
    myepisode.download(mydownloadqueue, display=display)
    assert display.change_status.call_count == 1

    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(DataFile.DATA_DIR,
                                                   "downloaded")
def test_perspective_downloaded_invert_episodes(display):
    perspective = get_downloaded_perspective(display)

    feed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    display.database.replace_feed(feed)
    display.database.replace_episodes(feed, feed.parse_episodes())
    display.menus_valid = False

    perspective._downloaded_menu.invert = mock.MagicMock()

    perspective._active_window = 0
    perspective._invert_selected_menu()
    assert perspective._downloaded_menu.invert.call_count == 1
    display._stdscr.reset_mock()
Beispiel #9
0
def test_database_feed_unplayed_episode_length(prevent_modification):
    copyfile(my_dir + "/datafiles/database_example1.db", Database.PATH)
    mydatabase = Database()
    myfeed_path = my_dir + "/feeds/valid_basic.xml"
    myfeed = Feed(file=myfeed_path)
    episodes = myfeed.parse_episodes()

    mydatabase.replace_feed(myfeed)
    mydatabase.replace_episode(myfeed, episodes[0])
    mydatabase.replace_episode(myfeed, episodes[1])
    assert len(mydatabase.unplayed_episodes(myfeed)) == 2
    feed_episodes = mydatabase.episodes(myfeed)
    feed_episodes[0].played = 1
    mydatabase.replace_episode(myfeed, feed_episodes[0])
    assert len(mydatabase.unplayed_episodes(myfeed)) == 1
Beispiel #10
0
def test_episode_delete(display):
    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(my_dir, "downloaded")
    episode_location = os.path.join(DataFile.DEFAULT_DOWNLOADED_DIR,
                                    "myfeed_title/myfeed_item2_title.mp3")
    with open(episode_location, "w") as file:
        file.write("temp file for test_episode.test_episode_delete")
    myfeed = Feed(file=my_dir + "/feeds/valid_enclosures.xml")
    display.change_status = mock.MagicMock(name="change_status")
    episode = myfeed.parse_episodes()[1]
    assert episode.downloaded
    episode.delete(display=display)
    assert display.change_status.call_count == 1
    assert not episode.downloaded

    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(DataFile.DATA_DIR,
                                                   "downloaded")
Beispiel #11
0
def test_episode_download():
    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(my_dir, "downloaded")
    mydownloadqueue = DownloadQueue()
    myfeed = Feed(file=my_dir + "/feeds/valid_basic.xml")
    myepisode = myfeed.parse_episodes()[1]
    DataFile.download_to_file = mock.MagicMock(name="download_to_file")
    myepisode.download(mydownloadqueue)

    successful = True
    for i in range(5000):
        if DataFile.download_to_file.call_count == 1:
            successful = True
            break

    DataFile.DEFAULT_DOWNLOADED_DIR = os.path.join(DataFile.DATA_DIR,
                                                   "downloaded")
    assert successful
def test_perspective_regression_11(display):
    perspective = get_primary_perspective(display)

    old_ddir = Config["custom_download_dir"]
    Config.data["custom_download_dir"] = os.path.join(my_dir, "downloaded")

    feed = Feed(file="%s/feeds/valid_enclosures.xml" % (my_dir))
    display.database.replace_feed(feed)
    display.database.replace_episodes(feed, feed.parse_episodes())
    display.menus_valid = False
    display.display()
    display.queue.clear()
    perspective._active_window = 1
    perspective._create_player_from_selected()

    assert display.queue[0]._path == os.path.join(my_dir, "downloaded",
                                                  "myfeed_title",
                                                  "myfeed_item1_title.mp3")

    Config.data["custom_download_dir"] = old_ddir
Beispiel #13
0
    def reload(self, display=None) -> None:
        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)

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

        if display is not None:
            display.change_status("Feeds successfully reloaded")
            display.menus_valid = False
Beispiel #14
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"
Beispiel #15
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
Beispiel #16
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
Beispiel #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)