2018-02-04 15:21:15 -08:00
|
|
|
import click
|
2018-05-29 13:28:23 -07:00
|
|
|
import sys
|
2018-06-02 11:18:05 -07:00
|
|
|
import os
|
2018-05-27 10:01:49 -07:00
|
|
|
|
|
|
|
import logging
|
|
|
|
|
2018-06-01 05:37:41 -07:00
|
|
|
from anime_downloader.sites import get_anime_class
|
2018-05-27 10:01:49 -07:00
|
|
|
from anime_downloader.sites.exceptions import NotFoundError
|
2018-06-01 01:13:44 -07:00
|
|
|
from anime_downloader.players.mpv import mpv
|
2018-06-03 10:30:26 -07:00
|
|
|
from anime_downloader.__version__ import __version__
|
2018-05-27 10:01:49 -07:00
|
|
|
|
2018-06-02 11:18:05 -07:00
|
|
|
from anime_downloader import util
|
|
|
|
from anime_downloader.config import Config
|
2018-05-31 04:04:47 -07:00
|
|
|
from anime_downloader import watch as _watch
|
2018-02-04 15:21:15 -08:00
|
|
|
|
|
|
|
echo = click.echo
|
|
|
|
|
2018-05-31 04:04:47 -07:00
|
|
|
|
2018-06-02 11:18:05 -07:00
|
|
|
@click.group(context_settings=Config.CONTEXT_SETTINGS)
|
2018-06-03 10:30:26 -07:00
|
|
|
@click.version_option(version=__version__)
|
2018-05-30 03:56:27 -07:00
|
|
|
def cli():
|
2018-05-30 04:29:23 -07:00
|
|
|
"""Anime Downloader
|
|
|
|
|
|
|
|
Download or watch your favourite anime
|
|
|
|
"""
|
2018-05-30 03:56:27 -07:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-05-31 04:04:47 -07:00
|
|
|
# NOTE: Don't put defaults here. Add them to the dict in config
|
2018-05-30 03:56:27 -07:00
|
|
|
@cli.command()
|
2018-02-04 15:21:15 -08:00
|
|
|
@click.argument('anime_url')
|
2018-06-04 23:37:48 -07:00
|
|
|
@click.option(
|
|
|
|
'--episodes', '-e', 'episode_range', metavar='<int>:<int>',
|
|
|
|
help="Range of anime you want to download in the form <start>:<end>")
|
|
|
|
@click.option(
|
|
|
|
'--url', '-u', 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(
|
|
|
|
'--skip-download', is_flag=True,
|
|
|
|
help="Retrieve without downloading")
|
|
|
|
@click.option(
|
|
|
|
'--download-dir', metavar='PATH',
|
|
|
|
help="Specifiy the directory to download to")
|
|
|
|
@click.option(
|
|
|
|
'--quality', '-q', type=click.Choice(['360p', '480p', '720p', '1080p']),
|
|
|
|
help='Specify the quality of episode. Default-720p')
|
|
|
|
@click.option(
|
|
|
|
'--force-download', '-f', is_flag=True,
|
|
|
|
help='Force downloads even if file exists')
|
|
|
|
@click.option(
|
|
|
|
'--log-level', '-ll', 'log_level',
|
|
|
|
type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR']),
|
|
|
|
help='Sets the level of logger')
|
2018-06-09 08:24:42 -07:00
|
|
|
@click.option(
|
|
|
|
'--name-fmt', '-nf',
|
|
|
|
help='Format for how the files to be downloaded be named.'
|
|
|
|
)
|
2018-05-31 04:04:47 -07:00
|
|
|
@click.pass_context
|
2018-06-03 02:12:03 -07:00
|
|
|
def dl(ctx, anime_url, episode_range, url, player, skip_download, quality,
|
2018-06-09 08:24:42 -07:00
|
|
|
force_download, log_level, download_dir, name_fmt):
|
2018-05-30 04:29:23 -07:00
|
|
|
""" Download the anime using the url or search for it.
|
2018-02-04 15:21:15 -08:00
|
|
|
"""
|
2018-05-28 12:36:40 -07:00
|
|
|
|
2018-05-27 10:01:49 -07:00
|
|
|
util.setup_logger(log_level)
|
2018-06-05 14:11:43 -07:00
|
|
|
util.print_info(__version__)
|
2018-05-27 10:01:49 -07:00
|
|
|
|
2018-06-01 05:37:41 -07:00
|
|
|
cls = get_anime_class(anime_url)
|
|
|
|
|
|
|
|
if not cls:
|
|
|
|
anime_url = util.search_and_get_url(anime_url)
|
|
|
|
cls = get_anime_class(anime_url)
|
2018-05-28 12:36:40 -07:00
|
|
|
|
2018-02-04 15:57:10 -08:00
|
|
|
try:
|
2018-06-04 23:37:48 -07:00
|
|
|
anime = cls(anime_url, quality=quality)
|
2018-02-04 15:57:10 -08:00
|
|
|
except NotFoundError as e:
|
|
|
|
echo(e.args[0])
|
|
|
|
return
|
2018-05-27 10:01:49 -07:00
|
|
|
if episode_range is None:
|
|
|
|
episode_range = '1:'+str(len(anime)+1)
|
2018-02-04 15:21:15 -08:00
|
|
|
|
2018-06-02 11:18:05 -07:00
|
|
|
logging.info('Found anime: {}'.format(anime.title))
|
|
|
|
|
2018-06-05 14:11:43 -07:00
|
|
|
anime = util.split_anime(anime, episode_range)
|
2018-06-09 08:24:42 -07:00
|
|
|
|
|
|
|
if url or player:
|
|
|
|
skip_download = True
|
|
|
|
|
|
|
|
if download_dir and not skip_download:
|
|
|
|
logging.info('Downloading to {}'.format(os.path.abspath(download_dir)))
|
|
|
|
|
|
|
|
for episode in anime:
|
|
|
|
if url:
|
|
|
|
util.print_episodeurl(episode)
|
|
|
|
|
|
|
|
if player:
|
|
|
|
util.play_episode(episode)
|
|
|
|
|
|
|
|
if not skip_download:
|
2018-06-09 08:24:49 -07:00
|
|
|
util.download_episode(episode, force_download=force_download,
|
|
|
|
download_dir=download_dir)
|
2018-06-09 08:24:42 -07:00
|
|
|
|
2018-05-28 12:36:40 -07:00
|
|
|
|
|
|
|
|
2018-05-30 03:56:27 -07:00
|
|
|
@cli.command()
|
2018-05-31 04:04:47 -07:00
|
|
|
@click.argument('anime_name', required=False)
|
2018-06-04 23:37:48 -07:00
|
|
|
@click.option(
|
|
|
|
'--new', '-n', type=bool, is_flag=True,
|
|
|
|
help="Create a new anime to watch")
|
|
|
|
@click.option(
|
|
|
|
'--list', '-l', '_list', type=bool, is_flag=True,
|
|
|
|
help="List all animes in watch list")
|
|
|
|
@click.option(
|
|
|
|
'--remove', '-r', 'remove', type=bool, is_flag=True,
|
|
|
|
help="Remove the specified anime")
|
|
|
|
@click.option(
|
|
|
|
'--quality', '-q', type=click.Choice(['360p', '480p', '720p', '1080p']),
|
|
|
|
help='Specify the quality of episode.')
|
|
|
|
@click.option(
|
|
|
|
'--log-level', '-ll', 'log_level',
|
|
|
|
type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR']),
|
|
|
|
help='Sets the level of logger', default='INFO')
|
2018-06-02 12:49:17 -07:00
|
|
|
def watch(anime_name, new, _list, quality, log_level, remove):
|
2018-06-01 12:14:57 -07:00
|
|
|
"""
|
2018-06-02 12:11:43 -07:00
|
|
|
With watch you can keep track of any anime you watch.
|
2018-06-05 14:11:43 -07:00
|
|
|
|
2018-06-09 08:24:42 -07:00
|
|
|
Available Commands after selection of an anime:\n
|
|
|
|
set : set episodes_done and title. Ex: set episodes_done=3\n
|
|
|
|
remove : remove selected anime from watch list\n
|
|
|
|
update : Update the episodes of the currrent anime\n
|
|
|
|
watch : Watch selected anime\n
|
|
|
|
download : Download episodes of selected anime
|
2018-06-01 12:14:57 -07:00
|
|
|
"""
|
2018-06-01 01:13:44 -07:00
|
|
|
util.setup_logger(log_level)
|
2018-06-05 14:11:43 -07:00
|
|
|
util.print_info(__version__)
|
|
|
|
|
2018-05-31 04:04:47 -07:00
|
|
|
watcher = _watch.Watcher()
|
2018-05-30 03:56:27 -07:00
|
|
|
|
2018-05-31 04:04:47 -07:00
|
|
|
if new:
|
|
|
|
if anime_name:
|
|
|
|
query = anime_name
|
|
|
|
else:
|
|
|
|
query = click.prompt('Enter a anime name or url', type=str)
|
2018-05-30 03:56:27 -07:00
|
|
|
|
2018-05-31 04:04:47 -07:00
|
|
|
url = util.search_and_get_url(query)
|
2018-05-28 12:36:40 -07:00
|
|
|
|
2018-05-31 04:04:47 -07:00
|
|
|
watcher.new(url)
|
|
|
|
sys.exit(0)
|
2018-05-28 12:36:40 -07:00
|
|
|
|
2018-06-02 12:49:17 -07:00
|
|
|
if remove:
|
|
|
|
anime = watcher.get(anime_name)
|
2018-06-02 13:04:39 -07:00
|
|
|
if anime and click.confirm(
|
|
|
|
"Remove '{}'".format(anime.title), abort=True
|
|
|
|
):
|
2018-06-05 14:11:43 -07:00
|
|
|
watcher.remove(anime)
|
2018-06-02 13:04:39 -07:00
|
|
|
else:
|
2018-06-04 23:37:48 -07:00
|
|
|
logging.error("Couldn't find '{}'. "
|
|
|
|
"Use a better search term.".format(anime_name))
|
2018-06-02 13:04:39 -07:00
|
|
|
sys.exit(1)
|
2018-06-02 12:49:17 -07:00
|
|
|
sys.exit(0)
|
|
|
|
|
2018-05-31 04:04:47 -07:00
|
|
|
if _list:
|
2018-06-05 14:11:43 -07:00
|
|
|
list_animes(watcher, quality)
|
2018-05-31 04:04:47 -07:00
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
if anime_name:
|
|
|
|
anime = watcher.get(anime_name)
|
2018-06-02 13:04:39 -07:00
|
|
|
if not anime:
|
|
|
|
logging.error(
|
2018-06-04 23:37:48 -07:00
|
|
|
"Couldn't find '{}'."
|
|
|
|
"Use a better search term.".format(anime_name))
|
2018-06-02 13:04:39 -07:00
|
|
|
sys.exit(1)
|
|
|
|
|
2018-06-02 12:11:43 -07:00
|
|
|
anime.quality = quality
|
2018-05-28 12:36:40 -07:00
|
|
|
|
2018-06-02 12:11:43 -07:00
|
|
|
logging.info('Found {}'.format(anime.title))
|
2018-06-05 14:11:43 -07:00
|
|
|
watch_anime(watcher, anime)
|
2018-06-03 05:43:19 -07:00
|
|
|
|
|
|
|
|
2018-06-05 14:11:43 -07:00
|
|
|
def list_animes(watcher, quality):
|
2018-06-03 05:43:19 -07:00
|
|
|
watcher.list()
|
|
|
|
inp = click.prompt('Select an anime', default=1)
|
|
|
|
try:
|
|
|
|
anime = watcher.get(int(inp)-1)
|
|
|
|
except IndexError:
|
|
|
|
sys.exit(0)
|
|
|
|
|
2018-06-05 14:11:43 -07:00
|
|
|
# Make the selected anime first result
|
|
|
|
watcher.update(anime)
|
|
|
|
|
2018-06-03 05:43:19 -07:00
|
|
|
while True:
|
|
|
|
click.clear()
|
2018-06-04 23:37:48 -07:00
|
|
|
click.secho('Title: ' + click.style(anime.title,
|
|
|
|
fg='green', bold=True))
|
2018-06-03 05:43:19 -07:00
|
|
|
click.echo('episodes_done: {}'.format(click.style(
|
|
|
|
str(anime.episodes_done), bold=True, fg='yellow')))
|
|
|
|
click.echo('Length: {}'.format(len(anime)))
|
|
|
|
|
|
|
|
meta = ''
|
|
|
|
for k, v in anime.meta.items():
|
|
|
|
meta += '{}: {}\n'.format(k, click.style(v, bold=True))
|
|
|
|
click.echo(meta)
|
|
|
|
|
2018-06-05 14:11:43 -07:00
|
|
|
click.echo('Available Commands: set, remove, update, watch\n')
|
2018-06-03 05:43:19 -07:00
|
|
|
|
|
|
|
inp = click.prompt('Press q to exit', default='q').strip()
|
|
|
|
|
2018-06-09 08:24:42 -07:00
|
|
|
# TODO: A better way to handle commands. Use regex. Refractor to class?
|
|
|
|
# Decorator?
|
2018-06-03 05:43:19 -07:00
|
|
|
if inp == 'q':
|
|
|
|
break
|
|
|
|
elif inp == 'remove':
|
2018-06-05 14:11:43 -07:00
|
|
|
watcher.remove(anime)
|
2018-06-03 05:43:19 -07:00
|
|
|
break
|
2018-06-05 14:11:43 -07:00
|
|
|
elif inp == 'update':
|
|
|
|
watcher.update_anime(anime)
|
|
|
|
elif inp == 'watch':
|
|
|
|
anime.quality = quality
|
|
|
|
watch_anime(watcher, anime)
|
|
|
|
sys.exit(0)
|
2018-06-09 08:24:42 -07:00
|
|
|
elif inp.startswith('download'):
|
|
|
|
try:
|
|
|
|
inp = inp.split('download ')[1]
|
|
|
|
except IndexError:
|
|
|
|
inp = ':'
|
|
|
|
inp = str(anime.episodes_done+1)+inp if inp.startswith(':') else inp
|
|
|
|
inp = inp+str(len(anime)) if inp.endswith(':') else inp
|
|
|
|
anime = util.split_anime(anime, inp)
|
|
|
|
for episode in anime:
|
|
|
|
util.download_episode(episode, force_download=False,
|
|
|
|
download_dir=Config['dl']['download_dir'])
|
2018-06-05 14:11:43 -07:00
|
|
|
elif inp.startswith('set '):
|
2018-06-03 05:43:19 -07:00
|
|
|
inp = inp.split('set ')[-1]
|
|
|
|
key, val = [v.strip() for v in inp.split('=')]
|
|
|
|
key = key.lower()
|
|
|
|
|
|
|
|
if key == 'title':
|
|
|
|
watcher.remove(anime)
|
|
|
|
setattr(anime, key, val)
|
|
|
|
watcher.add(anime)
|
|
|
|
elif key == 'episodes_done':
|
|
|
|
setattr(anime, key, int(val))
|
|
|
|
watcher.update(anime)
|
2018-06-05 14:11:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
def watch_anime(watcher, anime):
|
|
|
|
to_watch = anime[anime.episodes_done:]
|
|
|
|
|
|
|
|
for idx, episode in enumerate(to_watch):
|
|
|
|
|
|
|
|
for tries in range(5):
|
|
|
|
logging.info(
|
|
|
|
'Playing episode {}'.format(anime.episodes_done+1)
|
|
|
|
)
|
|
|
|
player = mpv(episode.stream_url)
|
|
|
|
returncode = player.play()
|
|
|
|
|
|
|
|
if returncode == mpv.STOP:
|
|
|
|
sys.exit(0)
|
|
|
|
elif returncode == mpv.CONNECT_ERR:
|
|
|
|
logging.warning("Couldn't connect. Retrying.")
|
|
|
|
continue
|
|
|
|
anime.episodes_done += 1
|
|
|
|
watcher.update(anime)
|
|
|
|
break
|