Beispiel #1
0
def config_init(path: str, force: bool) -> None:
    """Initialize a new configuration files."""
    create_config_dir_if_not_exists(path)
    validate_config_overwrite(path, force)

    new_config = get_new_config()
    new_config.save(path)
    logger.info("Successfully set the configuration!")
Beispiel #2
0
def config_show(is_json: bool) -> None:
    """Show all the available configuration."""
    result = _config.as_dict()
    if "password" in result:
        result["password"] = "******"
    if is_json:
        logger.info(json.dumps(result))
    else:
        logger.info(yaml.safe_dump(result))
Beispiel #3
0
def feed_import(opml_path: str, force: bool) -> None:
    """Import feeds from an OPML file."""
    feeds = convert_opml_to_dict(opml_path)

    validate_conflicts(feeds, force)

    config.feeds.update(feeds)
    config.save()

    logger.info("Successfully imported the feeds!")
Beispiel #4
0
def kindle_send(feed_title: str, url: str) -> None:
    """Send updates from one or all feeds (or a single article)."""
    validate_parser()
    logger.info(f"[Parsing articles with the `{config.parser}` parser]\n")
    if feed_title:
        send_articles_for_feed(feed_title)
    elif url:
        send_article_from_url(url)
    else:
        logger.notice("Sending articles from all feeds...\n")
        for feed_title in config.feeds:
            send_articles_for_feed(feed_title)
Beispiel #5
0
def feed_remove(title: str) -> None:
    """Remove a feed from the config."""
    if not title:
        feed_titles = list(config.feeds.keys())
        title, _ = pick(feed_titles, "Please choose the feed to remove")

    found = config.feeds.pop(title, False)
    config.save()

    if found:
        logger.info(f"Successfully removed `{title}` from the list of feeds")
    else:
        logger.info(f"Could not find `{title}` in the list of feeds")
Beispiel #6
0
def config_set(key: str, force: bool) -> None:
    """Set a value in the config."""
    if getattr(_config, key):
        if force:
            logger.warning("Overriding an existing value...")
        else:
            logger.error(
                f"A value already exists for `{key}`.\nPass the --force flag to overwrite it."
            )
            sys.exit(1)

    value = Prompt.get(key)

    setattr(_config, key, value)
    logger.info("Configuration successfully updated!")
Beispiel #7
0
def feed_add(title: str, url: str, force: bool) -> None:
    """Add an RSS feed."""
    validate_existing_feeds(title, force)

    feeds = get_feeds_from_url(url)

    if not feeds:
        logger.error("Could not find an RSS feed")
        sys.exit(1)
    elif len(feeds) == 1:
        feed = feeds[0]
    else:
        feed, _ = pick(feeds, "Please choose the correct feed from this list:")

    config.feeds[title] = {"url": feed}
    config.save()
    logger.info("Successfully added the feed!")
Beispiel #8
0
def get_feeds_from_url(url: str) -> list:
    """
    Try to parse the URL and find any RSS feeds in the webpage

    Adapted from: https://gist.github.com/alexmill/9bc634240531d81c3abe
    """
    logger.info(f"Attempting to find RSS feeds from {url}...")

    # If the URL itself is a proper RSS feed, just return it
    if is_rss_feed(url):
        logger.debug("URL is already a proper RSS feed")
        return [url]

    html = get_html(url)
    possible_feeds = get_feeds_from_links(html) + get_feeds_from_atags(
        url, html)

    return [url for url in set(possible_feeds) if is_rss_feed(url)]
Beispiel #9
0
def send_updates(unread_articles: List[Article], feed_title: str) -> None:
    """Iterate over `unread_articles`, and send each one to the kindle"""
    if unread_articles:
        if Parser(config.parser) == Parser.PUSH_TO_KINDLE:
            successful_count = send_urls([(article.title, article.link)
                                          for article in unread_articles])
        else:
            successful_count = send_epub_books(unread_articles, feed_title)

        if successful_count:
            logger.notice(
                f"Successfully sent {successful_count} articles from the `{feed_title}` feed!"
            )
        else:
            logger.error(
                f"Failed to send any articles to `{feed_title}`. See errors above"
            )
    else:
        logger.info(f"No new content for `{feed_title}`")
Beispiel #10
0
    def parse(self, parser: ParserBase) -> bool:
        """
        Prepare the content of the article for EPUB

        Performs these tasks:
            1. Parse the article with the Mercury parser
            2. Download all the images mentioned in the article (EPUB format only supports embedded local images)
            3. Replace all the <img "src"> tags with the local paths of the downloaded images
        :return: True iff the parsing succeeded
        """
        logger.info(f"Parsing `{self.title}`...")
        parsed_article = parser.parse(self.url)
        raw_content = parsed_article.get("content")
        if not raw_content:
            return False

        # When some blogs (like xkcd.com) are parsed,
        # their content doesn't include the actual image, so we're adding it here
        if lead_image_url := parsed_article.get("lead_image_url"):
            raw_content = f'<img src="{lead_image_url}"/>\n{raw_content}'
Beispiel #11
0
    def build(self) -> str:
        """
        Create an EPUB file from articles and templates

        :return: Path to the created epub archive
        """
        logger.info(f"Creating an EPUB book for `{self.title}`...")
        try:
            self.prepare_epub_dirs()
            self.copy_fixed_files()

            self.render_title()
            self.render_ncx_toc()
            self.render_html_toc()
            self.render_articles()
            self.render_opf()

            epub_path = self.compress_epub()
            logger.info("Successfully created an EPUB archive!")
            return epub_path
        finally:
            rmtree(self._dst_path)