diff --git a/anime_downloader/cli.py b/anime_downloader/cli.py index d67a02d..37da596 100644 --- a/anime_downloader/cli.py +++ b/anime_downloader/cli.py @@ -1,36 +1,56 @@ import click import subprocess + +import logging + from anime_downloader.sites.nineanime import NineAnime -from anime_downloader.sites.anime import NotFoundError +from anime_downloader.sites.exceptions import NotFoundError + +from . import util echo = click.echo @click.command() @click.argument('anime_url') -@click.option('--range', 'range_', metavar=':', + +@click.option('--episodes', '-e', 'episode_range', metavar=':', help="Range of anime you want to download in the form :") -@click.option('--playlist', default=False, type=bool, is_flag=True, - help="If flag is set, saves the stream urls in an m3u file") -@click.option('--url', default=False, type=bool, is_flag=True, - help="If flag is set, prints the stream url and not download") + +@click.option('--save-playlist', '-p', 'playlist', default=False, type=bool, is_flag=True, + help="If flag is set, saves the stream urls in an m3u file instead of downloading") + +@click.option('--url', '-u', default=False, type=bool, is_flag=True, + help="If flag is set, prints the stream url instead of downloading") + @click.option('--play', 'player', metavar='PLAYER', help="Streams in the specified player") + @click.option('--no-download', default=False, is_flag=True, help="Retrieve without downloading") -@click.option('--quality', type=click.Choice(['360p', '480p', '720p']), + +@click.option('--quality', '-q', type=click.Choice(['360p', '480p', '720p']), default='720p', help='Specify the quality of episode. Default-720p') -@click.option('--force', is_flag=True, default=False, + +@click.option('--force', '-f', is_flag=True, default=False, help='Force downloads even if file exists') -def cli(anime_url, range_, playlist, url, player, no_download, quality, force): + +@click.option('--log-level', '-ll', 'log_level', + type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR']), + default='INFO', + help='Sets the level of logger') + +def cli(anime_url, episode_range, playlist, url, player, no_download, quality, + force, log_level): """ Anime Downloader Download your favourite anime. """ + util.setup_logger(log_level) + try: - anime = NineAnime(anime_url, quality=quality, - callback=lambda message: print('[INFO] '+message)) + anime = NineAnime(anime_url, quality=quality) except NotFoundError as e: echo(e.args[0]) return @@ -38,15 +58,15 @@ def cli(anime_url, range_, playlist, url, player, no_download, quality, force): if url or player: no_download = True - if range is None: - range_ = '1:'+str(len(anime)+1) + if episode_range is None: + episode_range = '1:'+str(len(anime)+1) try: - start, end = [int(x) for x in range_.split(':')] + start, end = [int(x) for x in episode_range.split(':')] anime._episodeIds = anime._episodeIds[start-1:end-1] except ValueError: # Only one episode specified - anime = [anime[int(range_)-1]] + anime = [anime[int(episode_range)-1]] for episode in anime: if url: diff --git a/anime_downloader/sites/anime.py b/anime_downloader/sites/anime.py index 6e08a3c..242abd8 100644 --- a/anime_downloader/sites/anime.py +++ b/anime_downloader/sites/anime.py @@ -2,26 +2,21 @@ import requests from bs4 import BeautifulSoup import json import re + import time import sys import os import click +import logging - -class AnimeDLError(Exception): - pass - - -class URLError(AnimeDLError): - pass - - -class NotFoundError(AnimeDLError): - pass +from .exceptions import AnimeDLError class BaseAnime(): - def __init__(self, url, quality='720p', callback=None): + QUALITIES = None + _episodeClass = None + + def __init__(self, url, quality='720p'): if quality not in self.QUALITIES: raise AnimeDLError('Incorrect quality: "{}"'.format(quality)) @@ -33,9 +28,7 @@ class BaseAnime(): else: raise AnimeDLError(f'Quality {quality} not found in {self.QUALITIES}') - self._callback = callback - if self._callback: - self._callback('Extracting episode info from page') + logging.info('Extracting episode info from page') self.getEpisodes() def verify_url(self, url): @@ -52,7 +45,7 @@ class BaseAnime(): def __getitem__(self, index): ep_id = self._episodeIds[index] - return self._episodeClass(ep_id, self.quality, callback=self._callback) + return self._episodeClass(ep_id, self.quality) def _getEpisodeUrls(self, soup): return @@ -63,23 +56,21 @@ class BaseEpisode: title = '' stream_url = '' - def __init__(self, episode_id, quality='720p', callback=None): + def __init__(self, episode_id, quality='720p'): if quality not in self.QUALITIES: raise AnimeDLError('Incorrect quality: "{}"'.format(quality)) self.episode_id = episode_id self.quality = quality - self._callback = callback - if self._callback: - self._callback("Extracting stream info of id: {}".format(self.episode_id)) + logging.info("Extracting stream info of id: {}".format(self.episode_id)) self.getData() def getData(self): raise NotImplementedError def download(self, force=False): - print('[INFO] Downloading {}'.format(self.title)) + logging.info('Downloading {}'.format(self.title)) path = './' + self.title r = requests.get(self.stream_url, stream=True) @@ -89,19 +80,17 @@ class BaseEpisode: if os.path.exists(path) and not force: if os.stat(path).st_size == total_size: - print('[INFO] File already downloaded. Skipping download.') + logging.warning('File already downloaded. Skipping download.') return if r.status_code == 200: - with click.progressbar(length=int(total_size)) as bar: - with open(path, 'wb') as f: - for chunk in r.iter_content(chunk_size=chunksize): - if chunk: - f.write(chunk) - downloaded += chunksize - # write_status((downloaded), (total_size), - # start_time) - bar.update(chunksize) + with open(path, 'wb') as f: + for chunk in r.iter_content(chunk_size=chunksize): + if chunk: + f.write(chunk) + downloaded += chunksize + write_status((downloaded), (total_size), + start_time) def write_status(downloaded, total_size, start_time): @@ -113,5 +102,5 @@ def write_status(downloaded, total_size, start_time): status = 'Downloaded: {0:.2f}MB/{1:.2f}MB, Rate: {2:.2f}KB/s'.format( downloaded, total_size, rate) - sys.stdout.write("\r\n" + status + " "*5 + "\r") + sys.stdout.write("\r" + status + " "*5 + "\r") sys.stdout.flush() diff --git a/anime_downloader/sites/exceptions.py b/anime_downloader/sites/exceptions.py new file mode 100644 index 0000000..4808e6c --- /dev/null +++ b/anime_downloader/sites/exceptions.py @@ -0,0 +1,10 @@ +class AnimeDLError(Exception): + pass + + +class URLError(AnimeDLError): + pass + + +class NotFoundError(AnimeDLError): + pass diff --git a/anime_downloader/sites/nineanime.py b/anime_downloader/sites/nineanime.py index 73f609b..b7caeb0 100644 --- a/anime_downloader/sites/nineanime.py +++ b/anime_downloader/sites/nineanime.py @@ -1,4 +1,5 @@ -from .anime import BaseAnime, BaseEpisode, AnimeDLError, URLError, NotFoundError +from .anime import BaseAnime, BaseEpisode +from .exceptions import AnimeDLError, URLError, NotFoundError import json import requests from bs4 import BeautifulSoup @@ -7,6 +8,8 @@ import re import time +__all__ = ['NineAnimeEpisode', 'NineAnime'] + class NineAnimeEpisode(BaseEpisode): QUALITIES = ['360p', '480p', '720p'] _base_url = r'https://9anime.is/ajax/episode/info?id={id}&server={server}&_={param_}&ts={ts}' diff --git a/anime_downloader/util.py b/anime_downloader/util.py new file mode 100644 index 0000000..35202bc --- /dev/null +++ b/anime_downloader/util.py @@ -0,0 +1,12 @@ +import logging + +def setup_logger(log_level): + if log_level == 'DEBUG': + format = '%(levelname)s %(name)s: %(message)s' + else: + format = '%(levelname)s:%(message)s' + + logging.basicConfig( + level=logging.getLevelName(log_level), + format=format + )