def dl(ctx, anime_url, episode_range, url, player, skip_download, quality, force_download, log_level, download_dir): """ Download the anime using the url or search for it. """ util.setup_logger(log_level) util.print_info(__version__) cls = get_anime_class(anime_url) if not cls: anime_url = util.search_and_get_url(anime_url) cls = get_anime_class(anime_url) try: anime = cls(anime_url, quality=quality) except NotFoundError as e: echo(e.args[0]) return if episode_range is None: episode_range = '1:' + str(len(anime) + 1) logging.info('Found anime: {}'.format(anime.title)) anime = util.split_anime(anime, episode_range) util.process_anime(anime, player=player, force_download=force_download, download_dir=download_dir, url=url, skip_download=skip_download)
def dl(ctx, anime_url, episode_range, url, player, skip_download, quality, force_download, log_level, download_dir, file_format, provider, external_downloader): """ Download the anime using the url or search for it. """ util.setup_logger(log_level) util.print_info(__version__) cls = get_anime_class(anime_url) if not cls: anime_url = util.search(anime_url, provider) cls = get_anime_class(anime_url) try: anime = cls(anime_url, quality=quality) except Exception as e: echo(click.style(str(e), fg='red')) return # TODO: Refractor this somewhere else. (util?) if episode_range is None: episode_range = '1:' if episode_range.endswith(':'): episode_range += str(len(anime) + 1) if episode_range.startswith(':'): episode_range = '1' + episode_range logging.info('Found anime: {}'.format(anime.title)) anime = util.split_anime(anime, episode_range) 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, player=player) if not skip_download: if external_downloader: logging.info('Downloading episode {} of {}'.format( episode.ep_no, anime.title)) util.external_download(external_downloader, episode, file_format, path=download_dir) continue episode.download(force=force_download, path=download_dir, format=file_format) print()
def _get_anime_info_class(self, url): cls = get_anime_class(url) if not cls: logger.warn( f"The url: {url} is no longer supported. The provider needs to be set manually upon selection." ) """ Provides some level of backcompatability when watch lists have providers that have been removed. They are then warned via logger that they will have to change providers using the set function when an anime is selected in the list. """ url = ALL_ANIME_SITES[0][1] cls = get_anime_class(url) # TODO: Maybe this is better off as a mixin class AnimeInfo(cls, sitename=cls.sitename): def __init__(self, *args, **kwargs): self.episodes_done = kwargs.pop('episodes_done', 0) self._timestamp = kwargs.pop('timestamp', 0) # Initial values needed for MAL which can't be got yet from just a simple addition to the watch list. self.score = 0 self.watch_status = 'watching' self.colours = 'blue' self.mal_ID = 0 super(cls, self).__init__(*args, **kwargs) def progress(self): return (self.episodes_done, len(self)) return AnimeInfo
def command(ctx, anime_url, episode_range, url, player, skip_download, quality, force_download, download_dir, file_format, provider, external_downloader, chunk_size, disable_ssl, fallback_qualities, choice): """ Download the anime using the url or search for it. """ util.print_info(__version__) # TODO: Replace by factory cls = get_anime_class(anime_url) disable_ssl = cls and cls.__name__ == 'Masterani' or disable_ssl session.get_session().verify = not disable_ssl if not cls: anime_url = util.search(anime_url, provider, choice) cls = get_anime_class(anime_url) anime = cls(anime_url, quality=quality, fallback_qualities=fallback_qualities) logger.info('Found anime: {}'.format(anime.title)) animes = util.parse_ep_str(anime, episode_range) # TODO: # Two types of plugins: # - Aime plugin: Pass the whole anime # - Ep plugin: Pass each episode if url or player: skip_download = True if download_dir and not skip_download: logger.info('Downloading to {}'.format(os.path.abspath(download_dir))) for episode in animes: if url: util.print_episodeurl(episode) if player: util.play_episode(episode, player=player) if not skip_download: if external_downloader: logging.info('Downloading episode {} of {}'.format( episode.ep_no, anime.title)) util.external_download(external_downloader, episode, file_format, path=download_dir) continue if chunk_size is not None: chunk_size *= 1e6 chunk_size = int(chunk_size) with requests_cache.disabled(): episode.download(force=force_download, path=download_dir, format=file_format, range_size=chunk_size) print()
async def episode(ctx, number): async with ctx.typing(): str_id = str(ctx.author.id) f = Load(anime_config) if Int(number): if str_id in f: user = f[str_id] user["episode"] = int(number) - 1 Write(f, anime_config) Provider = get_anime_class(f[str_id].get( "provider", default_provider)) print(user["select"]["url"]) await ctx.send(f'Selected episode {number}') try: print(user["select"]["url"]) anime = Provider(user["select"]["url"]) embed = discord.Embed( title=user["select"]["title"] + f' - Episode {user["episode"]+1}/{len(anime)}', url=anime[user["episode"]].source().stream_url) except: await ctx.send('Error selecting episode') try: await ctx.send(embed=embed) except: await ctx.send(anime[user["episode"]].source().stream_url) else: await ctx.send(f'Invalid number: "{number}"')
async def _select(message, str_id, number): f = Load(anime_config) if str_id in f and Int(number): Provider = get_anime_class(f[str_id].get("provider", default_provider)) user = f[str_id] number = int(number) - 1 #Starts from 1 await message.channel.send('Selected: ' + user["data"][number]["title"] + f' - **Episode: {user["episode"]+1}**') user["select"]["url"], user["select"]["title"] = user["data"][number][ "url"], user["data"][number]["title"] Write(f, anime_config) try: anime = Provider(user["data"][number]["url"]) embed = discord.Embed( title=user["data"][number]["title"] + f' - Episode {user["episode"]+1}/{len(anime)}', url=anime[user["episode"]].source().stream_url) except: await message.channel.send('Error selecting episode') try: #Fix embed here await message.channel.send(embed=embed) except: await message.channel.send( anime[user["episode"]].source().stream_url)
def getDownloadFromUrl(url): print("Getting video meta-data for the url: {}".format(url)) kissAnimeInterface = get_anime_class('kissanime') currentAnime = kissAnimeInterface(url, quality="480p") currentAnimeName = currentAnime.title episodes = len(currentAnime) files = checkForAnimeYamlFile(os.getcwd()) if currentAnimeName.replace(" ", "-") + ".yaml" in files: episodeNum = getCurrentEpisodeFromYaml(files, currentAnimeName) else: episodeNum = -1 print("Found {} videos in this series to download. \n".format(episodes)) print("Downloading {} episdoes. This may take some time!".format(episodes)) if episodeNum == -1 or episodeNum == 0: print("Starting a new download session for `{}`".format( currentAnimeName)) normalDownload(episodes, currentAnime, currentAnimeName) # do a normal download as there is not a cache file here else: print("Resuming download session for `{}` at episode: {}".format( currentAnimeName, episodeNum)) downloadFromPos(episodes, currentAnime, currentAnimeName, episodeNum) # download from the current episode, rewriting the latest one just incase it is corrupt. # once the files have been downloaded move them to the correct directory moveVideosToSelectedDir(currentAnimeName, currentAnimeName.replace(" ", "-") + ".yaml")
def get_animes(self): # if nothing is selected it returns -1 # this makes the choice the first one if nothing is selected from search. if self.searchOutput.currentRow() != -1: choice = self.searchOutput.currentRow() + 1 else: choice = 1 start = self.animeEpisodeStart.text( ) if self.animeEpisodeStart.text().isnumeric() else 1 end = int(self.animeEpisodeEnd.text()) + \ 1 if self.animeEpisodeEnd.text().isnumeric() else '' episode_range = f'{start}:{end}' anime = self.animeName.text() provider = self.providers.currentText() print(anime, provider, choice) anime_url, _ = util.search(anime, provider, choice) cls = get_anime_class(anime_url) anime = cls(anime_url) ep_range = util.parse_episode_range(len(anime), episode_range) animes = util.split_anime(anime, ep_range) # animes = util.parse_ep_str(anime, episode_range) anime_title = anime.title # maybe make animes/anime_title self.animes? return animes, anime_title
def run(self): try: ani = get_anime_class(self.provider) self.search_result = ani.search(self.anime) if self.search_result: if self.verify: ratios = [[ fuzz.token_set_ratio(self.anime.lower(), sr.title.lower()), sr ] for sr in self.search_result] ratios = sorted(ratios, key=lambda x: x[0], reverse=True) end = len(ratios) for r in range(self.v_tries): if r == end: break try: anime_choice = ratios[r][1] anime_url = ani(anime_choice.url) stream_url = anime_url[0].source().stream_url self.exception = None break except Exception as e: self.exception = e self.search_result = util.format_search_results( self.search_result) except Exception as e: self.exception = e
async def _(event): if event.fwd_from: return input_str = event.pattern_match.group(1) lmao = input_str.split(":", 1) site = lmao[0] lol = lmao[1] why = site.lower() Twist = get_anime_class(why) search = Twist.search(lol) title1 = search[0].title url1 = search[0].url title2 = search[1].title url2 = search[1].url title3 = search[2].title url3 = search[2].url title4 = search[3].title url4 = search[3].url title5 = search[4].title url5 = search[4].url await event.edit( f"<b><u>Anime Search Complete</b></u> \n\n\n<b>Title</b>:- <code>{title1}</code> \n<b>URL Link</b>:- <code>{url1}</code>\n\n<b>Title</b>:- <code>{title2}</code> \n<b>URL Link</b>:- <code>{url2}</code>\n\n<b>Title</b>:- <code>{title3}</code> \n<b>URL Link</b>:- <code>{url3}</code>\n\n<b>Title</b>:- <code>{title4}</code> \n<b>URL Link</b>:- <code>{url4}</code>\n\n<b>Title</b>:- <code>{title5}</code> \n<b>URL Link</b>:- <code>{url5}</code>", parse_mode="HTML", )
def search(query, provider): # Since this function outputs to stdout this should ideally be in # cli. But it is used in watch too. :( cls = get_anime_class(provider) try: search_results = cls.search(query) except Exception as e: logging.error(click.style(str(e), fg='red')) sys.exit(1) click.echo(format_search_results(search_results)) if not search_results: logging.error('No such Anime found. Please ensure correct spelling.') sys.exit(1) val = click.prompt('Enter the anime no: ', type=int, default=1) try: url = search_results[val - 1].url title = search_results[val - 1].title except IndexError: logging.error('Only maximum of {} search results are allowed.' ' Please input a number less than {}'.format( len(search_results), len(search_results) + 1)) sys.exit(1) logging.info('Selected {}'.format(title)) return url
def fuzzy_match_metadata(seasons_info, search_results): # Gets the SearchResult object with the most similarity title-wise to the first MAL/Anilist result results = [] for i in seasons_info: for j in search_results: # Allows for returning of cleaned title by the provider using 'title_cleaned' in meta_info. # To make fuzzy matching better. title_provider = j.title.strip() if not j.meta_info.get('title_cleaned') else j.meta_info.get('title_cleaned').strip() # On some titles this will be None # causing errors below title_info = i.title # Essentially adds the chosen key to the query if the version is in use # Dirty solution, but should work pretty well config = Config['siteconfig'].get(get_anime_class(j.url).sitename, {}) version = config.get('version') version_use = version == 'dubbed' # Adds something like (Sub) or (Dub) to the title key_used = j.meta_info.get('version_key_dubbed', '') if version_use else j.meta_info.get('version_key_subbed', '') title_info += ' ' + key_used title_info = title_info.strip() # TODO add synonyms # 0 if there's no japanese name jap_ratio = fuzz.ratio(i.jp_title, j.meta_info['jp_title']) if j.meta_info.get('jp_title') else 0 # Outputs the max ratio for japanese or english name (0-100) ratio = max(fuzz.ratio(title_info, title_provider), jap_ratio) logger.debug('Ratio: {}, Info title: {}, Provider Title: {}, Key used: {}'.format(ratio, title_info, title_provider, key_used)) results.append(MatchObject(i, j, ratio)) # Returns the result with highest ratio return max(results, key=lambda item: item.ratio)
async def anime(client, message): pablo = await edit_or_reply(message, "`Searching For Anime.....`") anime = get_text(message) if not anime: await pablo.edit("`Please Give Me A Valid Input. You Can Check Help Menu To Know More!`") return lmao = anime.split(":", 1) try: site = lmao[1] except BaseException: site = "animeonline360" await pablo.edit( "Please Provide Site Name From Next Time. Now Continuing With Default Site." ) lol = lmao[0] why = site.lower() Twist = get_anime_class(why) try: search = Twist.search(lol) except BaseException: await ommhg.edit("Please Try Different Site. Given Site Is Down.") title1 = search[0].title url1 = search[0].url title2 = search[1].title url2 = search[1].url title3 = search[2].title url3 = search[2].url title4 = search[3].title url4 = search[3].url title5 = search[4].title url5 = search[4].url NopZ = f"<b><u>Anime Search Complete</b></u> \n\n\n<b>Title</b>:- <code>{title1}</code> \n<b>URL Link</b>:- {url1}\n\n<b>Title</b>:- <code>{title2}</code> \n<b>URL Link</b>:- {url2}\n\n<b>Title</b>:- <code>{title3}</code>\n<b>URL Link</b>:- {url3}\n\n<b>Title</b>:- <code>{title4}</code> \n<b>URL Link</b>:- {url4}\n\n<b>Title</b>:- <code>{title5}</code> \n<b>URL Link</b>:- {url5}\n\n<b>Links Gathered By Friday\nGet Your Own Friday From @FRIDAYCHAT</b>" await pablo.edit(NopZ, parse_mode="html")
def dl(ctx, anime_url, episode_range, url, player, skip_download, quality, force_download, log_level, download_dir, file_format, provider): """ Download the anime using the url or search for it. """ util.setup_logger(log_level) util.print_info(__version__) cls = get_anime_class(anime_url) if not cls: anime_url = util.search(anime_url, provider) cls = get_anime_class(anime_url) try: anime = cls(anime_url, quality=quality) except Exception as e: echo(click.style(str(e), fg='red')) return if episode_range is None: episode_range = '1:' + str(len(anime) + 1) logging.info('Found anime: {}'.format(anime.title)) anime = util.split_anime(anime, episode_range) 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, player=player) if not skip_download: episode.download(force=force_download, path=download_dir, format=file_format) print()
def hello(name, ep, provider, autoplay): Anime = get_anime_class(provider) player = 'mpv' searchResults = Anime.search(name) # click.echo(searchResults) anime = Anime(searchResults[0].url) print(anime) episode = anime[ep - 1] episode2 = anime[ep] click.echo(episode.source().stream_url) #click.echo(episode2.source().stream_url) click.echo(episode.source().referer) # util.play_episode(episode, player=player, title=f'{anime.title} - Episode {episode.ep_no}') # title=f'{anime.title} - Episode {episode.ep_no}' # title2=f'{anime.title} - Episode {episode2.ep_no}' # p = subprocess.Popen([ # player, # '--title={}'.format(title), # '--referrer="{}"'.format(episode.source().referer), # episode.source().stream_url, # '--title={}'.format(title2), # '--referrer="{}"'.format(episode2.source().referer), # episode2.source().stream_url # ]) tfile = tempfile.NamedTemporaryFile(mode='a+', suffix='.m3u8') mpvArgs = [ player, '--referrer={}'.format('https://twist.moe/'), '--playlist' ] if player == 'mpv': util.makePlaylist(anime[0:1], tfile) # for epi in anime: # title = f'{anime.title} - Episode {epi.ep_no}' # mpvArgs += ['--title={}'.format(title), # 'ffmpeg://{}'.format(epi.source().stream_url)] # click.echo("uai") print(tfile.name) mpvArgs.append(tfile.name) print(mpvArgs) tfile.seek(0) print(tfile.read()) #mpvArgs.append('{0} >/dev/null 2>&1 &') #subprocess.Popen("nohup usr/local/bin/otherscript.pl {0} >/dev/null 2>&1 &", shell=True) print(''.join(mpvArgs)) p = subprocess.Popen(mpvArgs) p.wait() util.addAnimesToPlaylist(anime[1:], tfile) print("humm") print(anime[0:1]) print(anime[1:]) print("uaaaaaaaaaaaaaaaaaaaaaaaa") else: p = subprocess.Popen([player, episode.source().stream_url]) p.wait()
def PrintResults(self): ''' This function will return the search outputs for the user to select what anime they want. ''' self.searchOutput.clear() cls = get_anime_class(self.providers.currentText()) searchResults = cls.search(self.animeName.text()) searchResults = [v.title for v in searchResults] self.searchOutput.addItems(searchResults) self.searchOutput.repaint()
async def provider(ctx, provider): async with ctx.typing(): f = Load(anime_config) str_id = str(ctx.author.id) if get_anime_class(provider): f[str_id]['provider'] = provider Write(f, anime_config) await ctx.send(f'Selected provider "{provider}"') else: await ctx.send( f'Invalid provider, choose from: {[a[0] for a in ALL_ANIME_SITES]}' )
def run(self): try: ani = get_anime_class(self.site) # this should be more dynamic sr = ani.search('naruto')[0] anime = ani(sr.url) stream_url = anime[0].source().stream_url except Exception as e: self.exception = e
def Search(query, Provider): Provider = get_anime_class(Provider) results = '' #results = '```' print(f'Query: {query}') search = Provider.search(query) for a in range(len(search)): """discord limit of 2000 chars, should ideally split the messages""" if len(f'{results}\n{str(search[a])}') + 3 < 1999: results = f'{results}\n{a+1}: {str(search[a])}' #results += '```' return (results, search)
def _get_anime_info_class(self, url): cls = get_anime_class(url) # TODO: Maybe this is better off as a mixin class AnimeInfo(cls): def __init__(self, *args, **kwargs): self.episodes_done = kwargs.pop('episodes_done', 0) self._timestamp = kwargs.pop('timestamp', 0) super(cls, self).__init__(*args, **kwargs) def progress(self): return (self.episodes_done, len(self)) return AnimeInfo
def search(query, provider, val=None, season_info=None, ratio=50): # Will use animeinfo sync if season_info is provided # Since this function outputs to stdout this should ideally be in # cli. But it is used in watch too. :( cls = get_anime_class(provider) search_results = cls.search(query) if not search_results: logger.error('No such Anime found. Please ensure correct spelling.') return None, None if season_info: from anime_downloader import animeinfo match = animeinfo.fuzzy_match_metadata([season_info], search_results) logger.debug('Match ratio: {}'.format(match.ratio)) # ratios are a range between 0-100 where 100 means 100% match. if match.ratio >= ratio and not val: logger.debug('Selected {}'.format(match.SearchResult.title)) return match.SearchResult.url, None click.echo(format_search_results(search_results), err=True) # Loop to allow re-propmt if the user chooses incorrectly # Makes it harder to unintentionally exit the anime command if it's automated while True: if val == None: val = click.prompt('Enter the anime no{}:'.format( ' (0 to switch provider)' * (season_info != None)), type=int, default=1, err=True) try: url = search_results[val - 1].url title = search_results[val - 1].title except IndexError: logger.error('Only maximum of {} search results are allowed.' ' Please input a number less than {}'.format( len(search_results), len(search_results) + 1)) val = False continue break # Doesn't print if skipped. if season_info is None or val != 0: logger.info('Selected {}'.format(title)) return url, val
def _get_anime_info_class(self, url): cls = get_anime_class(url) # TODO: Maybe this is better off as a mixin class AnimeInfo(cls, sitename=cls.sitename): def __init__(self, *args, **kwargs): self.episodes_done = kwargs.pop('episodes_done', 0) self._timestamp = kwargs.pop('timestamp', 0) self.score = 0 self.watch_status = 'watching' self.colours = 'blue' super(cls, self).__init__(*args, **kwargs) def progress(self): return (self.episodes_done, len(self)) return AnimeInfo
async def _(event): if event.fwd_from: return input_str = event.pattern_match.group(1) ommhg = await edit_or_reply(event, "Searching For Anime.....") lmao = input_str.split(":", 1) try: site = lmao[1] except: site = "animeonline360" await edit_or_reply( event, "Please Provide Site Name From Next Time. Now Continuing With Default Site." ) lol = lmao[0] why = site.lower() Twist = get_anime_class(why) try: search = Twist.search(lol) except: await ommhg.edit("Please Try Different Site. Given Site Is Down.") title1 = search[0].title url1 = search[0].url title2 = search[1].title url2 = search[1].url title3 = search[2].title url3 = search[2].url title4 = search[3].title url4 = search[3].url title5 = search[4].title url5 = search[4].url NopZ = f"<b><u>Anime Search Complete</b></u> \n\n\n<b>Title</b>:- <code>{title1}</code> \n<b>URL Link</b>:- {url1}\n\n<b>Title</b>:- <code>{title2}</code> \n<b>URL Link</b>:- {url2}\n\n<b>Title</b>:- <code>{title3}</code>\n<b>URL Link</b>:- {url3}\n\n<b>Title</b>:- <code>{title4}</code> \n<b>URL Link</b>:- {url4}\n\n<b>Title</b>:- <code>{title5}</code> \n<b>URL Link</b>:- {url5}\n\n<b>Links Gathered By Friday\nGet Your Own Friday From @FRIDAYCHAT</b>" await borg.send_message( event.chat_id, NopZ, parse_mode="HTML", ) await ommhg.delete()
async def _(event): if event.fwd_from: return input_str = event.pattern_match.group(1) searchthund = await event.reply("Searching Your Anime") inptstr = input_str.split(":", 1) try: site = inptstr[1] except: site = "Twist.moe" await event.reply("Searching from default website.") strzerooo = inptstr[0] chichidoyouloveme = site.lower() Twist = get_anime_class(chichidoyouloveme) try: search = Twist.search(strzerooo) except: await searchthund.edit("Error 404 Site is down.") title1 = search[0].title url1 = search[0].url title2 = search[1].title url2 = search[1].url title3 = search[2].title url3 = search[2].url title4 = search[3].title url4 = search[3].url title5 = search[4].title url5 = search[4].url await event.edit( f"<b><u>Anime Search Complete Say Thanks To ThunderUserbot</b></u> \n\n\n<b>Title</b>:- <code>{title1}</code> \n<b>URL Link</b>:- {url1}\n\n<b>Title</b>:- <code>{title2}</code> \n<b>URL Link</b>:- {url2}\n\n<b>Title</b>:- <code>{title3}</code>\n<b>URL Link</b>:- {url3}\n\n<b>Title</b>:- <code>{title4}</code> \n<b>URL Link</b>:- {url4}\n\n<b>Title</b>:- <code>{title5}</code> \n<b>URL Link</b>:- {url5}\n\n<b>Enjoy</b>", parse_mode="HTML", ) await searchthund.delete()
def get_episode(link, provider, parent): try: episode_class = AnimeEpisode.subclasses[provider] episode = episode_class(link, parent=DummyParent()) source = episode.source() return { "link": source.stream_url, "headers": { "referer": source.referer } } except KeyError: provider_ = get_anime_class(provider) search = provider_(parent) print(search) episode_class = AnimeEpisode.subclasses[provider] episode = episode_class(link, parent=DummyParent()) source = episode.source() return { "link": source.stream_url, "headers": { "referer": source.referer } }
def command(ctx, anime_url, episode_range, url, player, skip_download, quality, force_download, download_dir, file_format, provider, external_downloader, chunk_size, disable_ssl, fallback_qualities, choice, skip_fillers, speed_limit, sub, dub): """ Download the anime using the url or search for it. """ """if episode_range: regexed_range = re.compile("^:?(\d+)?:?(\d+)?$").search(episode_range) # Prevent such cases as: :5: and :1:1 if not regexed_range or (len(regexed_range.groups()) >= episode_range.count(":") and episode_range.count(":") != 1): raise click.UsageError( "Invalid value for '--episode' / '-e': {} is not a valid range".format(episode_range)) """ if sub and dub: raise click.UsageError( "--dub/-d and --sub/-s flags cannot be used together") query = anime_url[:] util.print_info(__version__) # TODO: Replace by factory cls = get_anime_class(anime_url) disable_ssl = cls and cls.__name__ == 'Masterani' or disable_ssl session.get_session().verify = not disable_ssl if not cls: anime_url, _ = util.search(anime_url, provider, choice) cls = get_anime_class(anime_url) subbed = None if sub or dub: subbed = subbed is not None anime = cls(anime_url, quality=quality, fallback_qualities=fallback_qualities, subbed=subbed) logger.info('Found anime: {}'.format(anime.title)) animes = util.parse_ep_str(anime, episode_range) if not animes: # Issue #508. raise exceptions.NotFoundError('No episodes found within index.') # TODO: # Two types of plugins: # - Aime plugin: Pass the whole anime # - Ep plugin: Pass each episode if url or player: skip_download = True if download_dir and not skip_download: logger.info('Downloading to {}'.format(os.path.abspath(download_dir))) if skip_fillers: fillers = util.get_filler_episodes(query) if speed_limit: logger.info("Speed is being limited to {}".format(speed_limit)) for episode in animes: if skip_fillers and fillers: if episode.ep_no in fillers: logger.info( "Skipping episode {} because it is a filler.".format( episode.ep_no)) continue if url: util.print_episodeurl(episode) if player: util.play_episode(episode, player=player, title=f'{anime.title} - Episode {episode.ep_no}') if not skip_download: if external_downloader: logging.info('Downloading episode {} of {}'.format( episode.ep_no, anime.title)) util.external_download(external_downloader, episode, file_format, speed_limit, path=download_dir) continue if chunk_size is not None: chunk_size *= 1e6 chunk_size = int(chunk_size) with requests_cache.disabled(): episode.download(force=force_download, path=download_dir, format=file_format, range_size=chunk_size) print()
def dl(ctx, anime_url, episode_range, url, player, skip_download, quality, force_download, log_level, download_dir, file_format, provider, external_downloader, chunk_size, disable_ssl, fallback_qualities): """ Download the anime using the url or search for it. """ util.setup_logger(log_level) util.print_info(__version__) cls = get_anime_class(anime_url) disable_ssl = cls and cls.__name__ == 'Masterani' or disable_ssl session.get_session().verify = not disable_ssl if not cls: anime_url = util.search(anime_url, provider) cls = get_anime_class(anime_url) try: anime = cls(anime_url, quality=quality, fallback_qualities=fallback_qualities) except Exception as e: if log_level != 'DEBUG': echo(click.style(str(e), fg='red')) else: raise return logging.info('Found anime: {}'.format(anime.title)) anime = util.parse_ep_str(anime, episode_range) 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, player=player) if not skip_download: if external_downloader: logging.info('Downloading episode {} of {}'.format( episode.ep_no, anime.title)) util.external_download(external_downloader, episode, file_format, path=download_dir) continue if chunk_size is not None: chunk_size *= 1e6 chunk_size = int(chunk_size) episode.download(force=force_download, path=download_dir, format=file_format, range_size=chunk_size) print()
def list_animes(watcher, quality, download_dir, imp=None, _filter=None): click.echo('Available Commands: swap, new') watcher.list(filt=_filter) inp = click.prompt('Select an anime', default="1") if not imp else imp provider = Config['watch']['provider'] # Not a number as input and command if not str(inp).isnumeric(): if ' ' in str(inp).strip(): args = command_parser(str(inp)) key = args[0].lower() vals = args[1:] if key == 'new': query = vals[0] if '--provider' in vals: if vals.index('--provider') + 1 < len(vals): provider = vals[vals.index('--provider') + 1] url, _ = util.search(query, provider) watcher.new(url) if key == 'swap': if vals[0] in [ 'all', 'watching', 'completed', 'planned', 'dropped', 'hold' ]: return list_animes(watcher, quality, download_dir, imp=imp, _filter=vals[0]) return list_animes(watcher, quality, download_dir, imp=imp) else: # Exits if neither int or actual command sys.exit(0) try: anime = watcher.get(int(inp) - 1) except IndexError: sys.exit(0) # Make the selected anime first result watcher.update(anime) while True: click.clear() click.secho('Title: ' + click.style(anime.title, fg='green', bold=True)) click.echo('episodes_done: {}'.format( click.style(str(anime.episodes_done), bold=True, fg='yellow'))) click.echo('Length: {}'.format(len(anime))) click.echo('Provider: {}'.format(anime.sitename)) click.echo('Score: {}'.format(anime.score)) click.echo('Watch Status: {}'.format(anime.watch_status)) meta = '' for k, v in anime.meta.items(): meta += '{}: {}\n'.format(k, click.style(str(v), bold=True)) click.echo(meta) click.echo('Available Commands: set, remove, update, watch, back,' ' download.\n') inp = click.prompt('Press q to exit', default='q').strip() # TODO: A better way to handle commands. Use regex. Refractor to class? # Decorator? if inp == 'q': sys.exit(0) elif inp == 'back': list_animes(watcher, quality, download_dir, imp=imp) elif inp == 'remove': watcher.remove(anime) list_animes(watcher, quality, download_dir, imp=imp) elif inp == 'update': watcher.update_anime(anime) elif inp == 'watch': anime.quality = quality watch_anime(watcher, anime, quality, download_dir) elif inp.startswith('watch '): anime.quality = quality watch_anime(watcher, anime, quality, download_dir, player_class=inp.split('watch ')[-1]) elif inp.startswith('download'): # You can use download 3:10 for selected episodes try: inp = inp.split('download ')[1] except IndexError: inp = ':' animes = util.parse_ep_str(anime, inp) # Using the config from dl. if not download_dir: download_dir = Config['dl']['download_dir'] # These things could be flags. external_downloader = Config['dl']['external_downloader'] file_format = Config['dl']['file_format'] speed_limit = Config['dl']['speed_limit'] for episode in animes: util.external_download(external_downloader, episode, file_format, speed_limit, path=download_dir) elif inp.startswith('set '): 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': # Retries if invalid input. if not val.isnumeric(): # Uncomment this if you want to let the user know. #logger.error("Invalid integer") # input() continue # Prevents setting length above max amount of episodes. val = val if int(val) <= len(anime) else len(anime) setattr(anime, key, int(val)) watcher.update(anime) elif key == 'provider': # Checks if it's an invalid provider preventing errors. if not get_anime_class(val): # Probably good to list providers here before looping. continue # Watch can quit if no anime is found, not ideal. url, _ = util.search(anime.title, val) watcher.remove(anime) newanime = watcher.new(url) newanime.episodes_done = anime.episodes_done newanime.score = anime.score newanime.watch_status = anime.watch_status newanime._timestamp = anime._timestamp watcher.update(newanime) anime = newanime elif key == 'score': anime.score = val watcher.update(anime) elif key == 'watch_status': if val in [ 'watching', 'completed', 'dropped', 'planned', 'all' ]: colours = { 'watching': 'cyan', 'completed': 'green', 'dropped': 'red', 'planned': 'yellow', 'hold': 'white' } anime.watch_status = val anime.colours = colours.get(anime.watch_status, 'yellow') watcher.update(anime)
def command(ctx, anime_url, episode_range, url, player, skip_download, quality, force_download, download_dir, file_format, provider, external_downloader, chunk_size, disable_ssl, fallback_qualities, choice, skip_fillers, speed_limit): """ Download the anime using the url or search for it. """ query = anime_url[:] util.print_info(__version__) # TODO: Replace by factory cls = get_anime_class(anime_url) disable_ssl = cls and cls.__name__ == 'Masterani' or disable_ssl session.get_session().verify = not disable_ssl if not cls: anime_url, _ = util.search(anime_url, provider, choice) cls = get_anime_class(anime_url) anime = cls(anime_url, quality=quality, fallback_qualities=fallback_qualities) logger.info('Found anime: {}'.format(anime.title)) animes = util.parse_ep_str(anime, episode_range) if not animes: # Issue #508. raise exceptions.NotFoundError('No episodes found within index.') # TODO: # Two types of plugins: # - Aime plugin: Pass the whole anime # - Ep plugin: Pass each episode if url or player: skip_download = True if download_dir and not skip_download: logger.info('Downloading to {}'.format(os.path.abspath(download_dir))) if skip_fillers: fillers = util.get_filler_episodes(query) if speed_limit: logger.info("Speed is being limited to {}".format(speed_limit)) for episode in animes: if skip_fillers and fillers: if episode.ep_no in fillers: logger.info("Skipping episode {} because it is a filler.".format(episode.ep_no)) continue if url: util.print_episodeurl(episode) if player: util.play_episode(episode, player=player, title=f'{anime.title} - Episode {episode.ep_no}') if not skip_download: if external_downloader: logging.info('Downloading episode {} of {}'.format( episode.ep_no, anime.title) ) util.external_download(external_downloader, episode, file_format, speed_limit, path=download_dir) continue if chunk_size is not None: chunk_size *= 1e6 chunk_size = int(chunk_size) with requests_cache.disabled(): episode.download(force=force_download, path=download_dir, format=file_format, range_size=chunk_size) print()
def command(ctx, anime_url, episode_range, player, force_download, provider, skip_fillers, ratio, url, choice, download_metadata): """ dl with fallback providers\n Will use another provider even if the chosen one fails.\n """ # Borrows some config from the original dl command. # This can all be flags, but ezdl is made to be easy. fallback_qualities = Config['dl']['fallback_qualities'] download_dir = Config['dl']['download_dir'] quality = Config['dl']['quality'] url = Config['dl']['url'] if not url else url skip_download = Config['dl']['skip_download'] chunk_size = Config['dl']['chunk_size'] speed_limit = Config['dl']['speed_limit'] external_downloader = Config['dl']['external_downloader'] file_format = Config['ezdl']['file_format'] fallback_providers = Config['ezdl']['fallback_providers'] query = anime_url[:] util.print_info(__version__) fallback_providers.insert(0, provider) # Eliminates duplicates while keeping order providers = sorted(set(fallback_providers), key=fallback_providers.index) info = animeinfo.search_anilist(query, choice) logger.info('Selected "{}" '.format(info.title)) episode_count = info.episodes - 1 # Interprets the episode range for use in a for loop. # 1:3 -> for _episode in range(1, 4): episode_range = util.parse_episode_range(episode_count, episode_range) episode_range_split = episode_range.split(':') # Issue #508. if episode_range_split[0] > episode_range_split[-1]: raise exceptions.NotFoundError('No episodes found within index.') # Stores the choices for each provider, to prevent re-prompting search. # As the current setup runs episode wise without this a 12 episode series would give 12+ prompts. choice_dict = {} # Doesn't work on nyaa since it only returns one episode. for episode_range in range(int(episode_range_split[0]), int(episode_range_split[-1]) + 1): # Exits if all providers are skipped. if [choice_dict[i] for i in choice_dict] == [0] * len(providers): logger.info('All providers skipped, exiting') exit() for provider in providers: if not get_anime_class(provider): logger.info('"{}" is an invalid provider'.format(provider)) continue logger.debug('Current provider: {}'.format(provider)) # TODO: Replace by factory cls = get_anime_class(anime_url) # To make the downloads use the correct name if URL:s are used. real_provider = cls.sitename if cls else provider # This will allow for animeinfo metadata in filename and one filename for multiple providers. rep_dict = { 'animeinfo_anime_title': util.slugify(info.title), 'provider': util.slugify(real_provider), 'anime_title': '{anime_title}', 'ep_no': '{ep_no}' } fixed_file_format = file_format.format(**rep_dict) # Keeping this as I don't know the impact of removing it. # It's False by default in normal dl. disable_ssl = False session.get_session().verify = not disable_ssl # This is just to make choices in providers presistent between searches. choice_provider = choice_dict.get(provider) if not cls: _anime_url, choice_provider = util.search(anime_url, provider, val=choice_provider, season_info=info, ratio=ratio) choice_dict[provider] = choice_provider if choice_provider == 0 or not _anime_url: logger.info('Skipped') continue cls = get_anime_class(_anime_url) try: anime = cls(_anime_url, quality=quality, fallback_qualities=fallback_qualities) # I have yet to investigate all errors this can output # No sources found gives error which exits the script except: continue logger.debug('Found anime: {}'.format(anime.title)) try: animes = util.parse_ep_str(anime, str(episode_range)) except RuntimeError: logger.error( 'No episode found with index {}'.format(episode_range)) continue except: logger.error('Unknown provider error') continue # TODO: # Two types of plugins: # - Aime plugin: Pass the whole anime # - Ep plugin: Pass each episode if url or player: skip_download = True if download_dir and not skip_download: logger.info('Downloading to {}'.format( os.path.abspath(download_dir))) if skip_fillers: fillers = util.get_filler_episodes(query) for episode in animes: if skip_fillers and fillers: if episode.ep_no in fillers: logger.info( "Skipping episode {} because it is a filler.". format(episode.ep_no)) continue if download_metadata: util.download_metadata(fixed_file_format, info.metadata, episode) if url: util.print_episodeurl(episode) if player: util.play_episode( episode, player=player, title=f'{anime.title} - Episode {episode.ep_no}') if not skip_download: if external_downloader: logging.info('Downloading episode {} of {}'.format( episode.ep_no, anime.title)) util.external_download(external_downloader, episode, fixed_file_format, path=download_dir, speed_limit=speed_limit) continue if chunk_size is not None: chunk_size = int(chunk_size) chunk_size *= 1e6 with requests_cache.disabled(): episode.download(force=force_download, path=download_dir, format=fixed_file_format, range_size=chunk_size) print() # If it's all successfull proceeds to next ep instead of looping. break