def test_check_sources_random(self): iterations = 0 animes = get_animes() while iterations <= MAX_SOURCE_TEST_ITERATION: iterations += 1 random_anime = random.choice(animes) current_sources = get_sources(random_anime) if current_sources: source = random.choice(current_sources) url = urljoin(TWIST_CDN_URL, source.source) try: client = get_auth_client() r = client.get( url, headers={ **dict(client.headers), "referer": TWIST_URL, "range": "bytes=0-10", }, ) r.raise_for_status() except httpx.HTTPError as e: print( f"Error on iteration n°{iterations} for {random_anime.full_title()}\n\tepisode={source.number}" f"{e}\n") continue print( f"Found source for {random_anime.full_title()}\n\tepisode={source.number}\n\turl={r.url}" ) break assert iterations <= 100
def test_get_animes(self): animes = get_animes() assert len(animes) > 1000 naruto_anime = list(filter(lambda a: a.slug.slug == "naruto", animes))[0] assert naruto_anime.title == "Naruto" assert naruto_anime.id == 451
def filter_animes( search: str, animes: Optional[List[Anime]] = None, threshold: int = FUZZY_SEARCH_THRESHOLD, limit: int = FUZZY_SEARCH_MAX_RESULTS, ): if animes is None: animes = get_animes() animes_by_title = dict( sorted(((anime.title, anime) for anime in animes), key=lambda x: x[0]) ) animes_by_alt_title = dict( sorted( ((anime.alt_title, anime) for anime in animes if anime.alt_title), key=lambda x: x[0], ) ) selected_animes_with_score_by_id = {} for anime in animes_by_title.values(): score = fuzz.token_set_ratio(search.lower(), anime.title.lower()) if score > threshold: selected_animes_with_score_by_id[anime.id] = (score, anime) for anime in animes_by_alt_title.values(): score = fuzz.token_set_ratio(search.lower(), anime.alt_title.lower()) if score > threshold: selected_animes_with_score_by_id[anime.id] = (score, anime) return list( x[1] for x in sorted( selected_animes_with_score_by_id.values(), key=lambda x: x[0], reverse=True, ) )[:limit]
def get_choices(context: Dict[str, str]) -> List[Dict[str, Union[str, Anime]]]: animes = get_animes() if context.get("filter"): animes = filter_animes(context["filter"], animes=animes) return [ {"name": anime.full_title(), "value": anime} for anime in sorted((a for a in animes), key=lambda a: a.title) ]
def display_anime_details(slug: str = typer.Argument( None, help=ANIME_SLUG_HELP, callback=select_anime_slug), ): """ Give more details on a specific anime like the number of episodes from a given anime slug """ animes = get_animes() animes_by_slug = {anime.slug.slug: anime for anime in animes} try: anime = animes_by_slug[slug] except KeyError: raise typer.BadParameter(invalid_slug_message(slug=slug, animes=animes)) typer.echo(anime_details_message(get_anime_details(anime)))
def display_animes(search: Optional[str] = typer.Option( None, help="Filter results with fuzzy search")): """ Search and display information about available animes and optionally use --search to fuzzy search Among the information given, the slug is what is used in other commands """ animes = get_animes() if search: animes = filter_animes(search, animes) for anime in animes: typer.echo(anime_message(anime))
def download( slug: str = typer.Argument(None, help=ANIME_SLUG_HELP, callback=select_anime_slug), directory: str = typer.Option( CWD_DIR, "--d", help="Directory where files will be uploaded"), nfrom: int = typer.Option( None, "--nfrom", help="Select episodes greater or equal to the given number"), nto: int = typer.Option( None, "--nto", help="Select episodes lesser or equal to the given number"), dfrom: datetime.datetime = typer.Option( None, "--dfrom", help="Select episodes uploaded after the given date"), dto: datetime.datetime = typer.Option( None, "--dto", help="Select episodes uploaded before the given date"), ): """ Download a list of episodes from a given anime slug The output directory can be specified """ directory = Path(directory) animes = get_animes() animes_by_slug = {anime.slug.slug: anime for anime in animes} try: anime = animes_by_slug[slug] except KeyError: raise typer.BadParameter(invalid_slug_message(slug=slug, animes=animes)) sources = get_sources(anime) filtered_source_ids = set(source.id for source in sources) if nfrom: if type(nfrom) is int: filtered_source_ids -= set(source.id for source in sources if source.number < nfrom) if dfrom: filtered_source_ids -= set(source.id for source in sources if source.created_at < dfrom) if nto: filtered_source_ids -= set(source.id for source in sources if source.number > nto) if dto: filtered_source_ids -= set(source.id for source in sources if source.created_at > dto) sources = list( sorted( (source for source in sources if source.id in filtered_source_ids), key=lambda s: s.number, )) if not sources: typer.secho("No sources to download ! ", bold=True) raise typer.Exit(code=1) typer.echo(download_starting_message(sources=sources, directory=directory)) for source in sources: typer.echo( f"{anime.title} - S{anime.season:02d} - E{source.number:02d}") title_slug = slugify(anime.title) current_dir = directory / title_slug if not current_dir.exists(): current_dir.mkdir(parents=True, exist_ok=True) ext = source.source.rsplit(".", 1)[-1] filepath = ( current_dir / f"{title_slug}-S{anime.season:02d}-E{source.number:03d}.{ext}") download_source(source, filepath)