<reponame>musflood/manga-gui<gh_stars>0
"""A source website from which to pull manga."""
import re
import urllib

import requests


class MangaSource(object):
    """Details for a source website.

    Attributes:
        name: Readable name for the source.
        root_url: URL for a series index, without the title.
        slug_filler: Character used for spaces in the slug.

        is_multipage: Whether the source has each page image on its
            own webpage or not.
        pg_img_attrs: Additional attributes on the HTML tag that contains
            the page image.
        index_tag: The HTML tag that contains the table of contents on
            the index page for a series.
        index_attrs: Additional attributes on the HTML tag that contains
            the table of contents on the index page for a series.

    """

    def __init__(self, name, root_url, slug_filler, is_multipage=True,
                 pg_img_attrs=None, index_tag='table', index_attrs=None):
        """Set up details for a new source.

        Args:
            name: Name to be displayed to the user.
            root_url: URL for a series index, without the title.
            slug_filler: Character used for spaces in the slug for a title.

            is_multipage: (optional) Whether the source has each page image
                on its own webpage or not.
            pg_img_attrs: (optional) Additional attributes on the HTML tag
                that contains the page image. Must be provided in a dict.
            index_tag: (optional) The HTML tag that contains the table of
                contents on the index page for a series.
            index_attrs: (optional) Additional attributes on the HTML tag
                that contains the table of contents on the index page
                for a series. Must be provided in a dict.

        Raises:
            TypeError: For non-string arguments.
            ValueError: For empty name or root_url and for invalid root_url.

        """
        if not all(type(arg) is str for arg in
                   (name, root_url, slug_filler, index_tag)):
            raise TypeError('Must construct a MangaSource with strings.')

        if not all((name, root_url, index_tag)):
            raise ValueError('Cannot use empty strings for a MangaSource.')

        if pg_img_attrs is not None and type(pg_img_attrs) is not dict:
            raise TypeError('Tag attributes must be given as a dict.')

        if index_attrs is not None and type(index_attrs) is not dict:
            raise TypeError('Tag attributes must be given as a dict.')

        # Fundamental source identifiers
        self.name = name

        if not root_url.startswith('http'):
            root_url = 'http://' + root_url
        if not root_url.endswith('/'):
            root_url += '/'

        valid_url = re.fullmatch(r'^https?://[^\s/$.?#].[^\s]*$',
                                 root_url, flags=re.I)
        if not valid_url:
            raise ValueError(f'{root_url} if not a valid url.')

        self.root_url = root_url
        self._verified = self.ping()

        self.slug_filler = slug_filler

        # Details for parsing page and chapter HTML
        self.is_multipage = is_multipage

        self.pg_img_attrs = pg_img_attrs if pg_img_attrs else {}

        self.index_tag = index_tag
        self.index_attrs = index_attrs if index_attrs else {}

    def __repr__(self):
        """Display the name and url for the source."""
        return f'<MangaSource: {self.name} @ {self.root_url}>'

    def __str__(self):
        """Display the name of the source."""
        return self.name

    def _slugify(self, s):
        """Get the slug of the given string for the URL."""
        if type(s) is not str:
            raise TypeError('Can only slugify a string.')

        clean_s = re.sub(r'[^0-9a-zA-Z ]', '', s.strip().lower())
        return clean_s.replace(' ', self.slug_filler)

    def index_url(self, title):
        """Get the URL for the index of the given title."""
        if type(title) is not str:
            raise TypeError('Manga title must be a string.')

        slug = self._slugify(title)
        return urllib.parse.urljoin(self.root_url, slug)

    def ping(self):
        """Ping the source website to verify it is online.

        Returns:
            True for 200 and 300 level status codes.
            False for 400 and 500 level status codes and for
            timeout or other errors.

        """
        try:
            response = requests.head(self.root_url)
        except requests.exceptions.RequestException:
            return False

        if response.status_code >= 400:
            return False

        return True

"""A source website from which to pull manga."""
import re
import urllib

import requests


class MangaSource(object):
    """Details for a source website.

    Attributes:
        name: Readable name for the source.
        root_url: URL for a series index, without the title.
        slug_filler: Character used for spaces in the slug.

        is_multipage: Whether the source has each page image on its
            own webpage or not.
        pg_img_attrs: Additional attributes on the HTML tag that contains
            the page image.
        index_tag: The HTML tag that contains the table of contents on
            the index page for a series.
        index_attrs: Additional attributes on the HTML tag that contains
            the table of contents on the index page for a series.

    """

    def __init__(self, name, root_url, slug_filler, is_multipage=True,
                 pg_img_attrs=None, index_tag='table', index_attrs=None):
        """Set up details for a new source.

        Args:
            name: Name to be displayed to the user.
            root_url: URL for a series index, without the title.
            slug_filler: Character used for spaces in the slug for a title.

            is_multipage: (optional) Whether the source has each page image
                on its own webpage or not.
            pg_img_attrs: (optional) Additional attributes on the HTML tag
                that contains the page image. Must be provided in a dict.
            index_tag: (optional) The HTML tag that contains the table of
                contents on the index page for a series.
            index_attrs: (optional) Additional attributes on the HTML tag
                that contains the table of contents on the index page
                for a series. Must be provided in a dict.

        Raises:
            TypeError: For non-string arguments.
            ValueError: For empty name or root_url and for invalid root_url.

        """
        if not all(type(arg) is str for arg in
                   (name, root_url, slug_filler, index_tag)):
            raise TypeError('Must construct a MangaSource with strings.')

        if not all((name, root_url, index_tag)):
            raise ValueError('Cannot use empty strings for a MangaSource.')

        if pg_img_attrs is not None and type(pg_img_attrs) is not dict:
            raise TypeError('Tag attributes must be given as a dict.')

        if index_attrs is not None and type(index_attrs) is not dict:
            raise TypeError('Tag attributes must be given as a dict.')

        # Fundamental source identifiers
        self.name = name

        if not root_url.startswith('http'):
            root_url = 'http://' + root_url
        if not root_url.endswith('/'):
            root_url += '/'

        valid_url = re.fullmatch(r'^https?://[^\s/$.?#].[^\s]*$',
                                 root_url, flags=re.I)
        if not valid_url:
            raise ValueError(f'{root_url} if not a valid url.')

        self.root_url = root_url
        self._verified = self.ping()

        self.slug_filler = slug_filler

        # Details for parsing page and chapter HTML
        self.is_multipage = is_multipage

        self.pg_img_attrs = pg_img_attrs if pg_img_attrs else {}

        self.index_tag = index_tag
        self.index_attrs = index_attrs if index_attrs else {}

    def __repr__(self):
        """Display the name and url for the source."""
        return f'<MangaSource: {self.name} @ {self.root_url}>'

    def __str__(self):
        """Display the name of the source."""
        return self.name

    def _slugify(self, s):
        """Get the slug of the given string for the URL."""
        if type(s) is not str:
            raise TypeError('Can only slugify a string.')

        clean_s = re.sub(r'[^0-9a-zA-Z ]', '', s.strip().lower())
        return clean_s.replace(' ', self.slug_filler)

    def index_url(self, title):
        """Get the URL for the index of the given title."""
        if type(title) is not str:
            raise TypeError('Manga title must be a string.')

        slug = self._slugify(title)
        return urllib.parse.urljoin(self.root_url, slug)

    def ping(self):
        """Ping the source website to verify it is online.

        Returns:
            True for 200 and 300 level status codes.
            False for 400 and 500 level status codes and for
            timeout or other errors.

        """
        try:
            response = requests.head(self.root_url)
        except requests.exceptions.RequestException:
            return False

        if response.status_code >= 400:
            return False

        return True
A source website from which to pull manga. Details for a source website.      Attributes:         name: Readable name for the source.         root_url: URL for a series index, without the title.         slug_filler: Character used for spaces in the slug.          is_multipage: Whether the source has each page image on its             own webpage or not.         pg_img_attrs: Additional attributes on the HTML tag that contains             the page image.         index_tag: The HTML tag that contains the table of contents on             the index page for a series.         index_attrs: Additional attributes on the HTML tag that contains             the table of contents on the index page for a series. Set up details for a new source.          Args:             name: Name to be displayed to the user.             root_url: URL for a series index, without the title.             slug_filler: Character used for spaces in the slug for a title.              is_multipage: (optional) Whether the source has each page image                 on its own webpage or not.             pg_img_attrs: (optional) Additional attributes on the HTML tag                 that contains the page image. Must be provided in a dict.             index_tag: (optional) The HTML tag that contains the table of                 contents on the index page for a series.             index_attrs: (optional) Additional attributes on the HTML tag                 that contains the table of contents on the index page                 for a series. Must be provided in a dict.          Raises:             TypeError: For non-string arguments.             ValueError: For empty name or root_url and for invalid root_url. # Fundamental source identifiers #].[^\s]*$', # Details for parsing page and chapter HTML Display the name and url for the source. Display the name of the source. Get the slug of the given string for the URL. Get the URL for the index of the given title. Ping the source website to verify it is online.          Returns:             True for 200 and 300 level status codes.             False for 400 and 500 level status codes and for             timeout or other errors.