mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-07 05:10:39 -08:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83c98936d1 | ||
|
|
0891cb279a | ||
|
|
95ba96f537 | ||
|
|
586790173b | ||
|
|
1d19449ab7 | ||
|
|
e1f73334ef | ||
|
|
4faac017b5 | ||
|
|
bfbd2a57a0 | ||
|
|
9519472f83 | ||
|
|
5c0c119cbc | ||
|
|
87eb257a10 | ||
|
|
4a08076c3b | ||
|
|
0d239e6793 | ||
|
|
0a0d47ae88 | ||
|
|
2ba07d47b3 | ||
|
|
f1b520fe3c | ||
|
|
8cfcc26468 | ||
|
|
cd51edf0b8 | ||
|
|
6eb28cfa3d | ||
|
|
542d39fa6a | ||
|
|
e5e328148f |
33
README.md
33
README.md
@@ -196,20 +196,22 @@ Overview of main commands:
|
|||||||
|
|
||||||
Configuration is directly passed into this command at run time to override your config.
|
Configuration is directly passed into this command at run time to override your config.
|
||||||
|
|
||||||
Available options include:
|
Available options for the fastanime command include:
|
||||||
|
|
||||||
- `--server;-s <server>` set the default server to auto select
|
- `--server <server>` or `-s <server>` set the default server to auto select
|
||||||
- `--continue;-c/--no-continue;-no-c` whether to continue from the last episode you were watching
|
- `--continue/--no-continue` or `-c/-no-c` whether to continue from the last episode you were watching
|
||||||
- `--quality;-q <0|1|2|3>` the link to choose from server
|
- `--quality <1080/720/480/360>` or `-q <1080/720/480/360>` the link to choose from server
|
||||||
- `--translation-type;- <dub|sub` what language for anime
|
- `--translation-type <dub/sub>` or `-t <dub/sub>` what language for anime
|
||||||
- `--auto-select;-a/--no-auto-select;-no-a` auto select title from provider results
|
- `--dub` dubbed anime
|
||||||
- `--auto-next;-A;/--no-auto-next;-no-A` auto select next episode
|
- `--sub` subbed anime
|
||||||
- `-downloads-dir;-d <path>` set the folder to download anime into
|
- `--auto-select/--no-auto-select` or `-a/-no-a` auto select title from provider results
|
||||||
|
- `--auto-next/--no-auto-next` or `-A/-no-A` auto select next episode
|
||||||
|
- `-downloads-dir <path>` or `-d <path>` set the folder to download anime into
|
||||||
- `--fzf` use fzf for the ui
|
- `--fzf` use fzf for the ui
|
||||||
- `--default` use the default ui
|
- `--default` use the default ui
|
||||||
- `--preview` show a preview when using fzf
|
- `--preview` show a preview when using fzf
|
||||||
- `--no-preview` dont show a preview when using fzf
|
- `--no-preview` dont show a preview when using fzf
|
||||||
- `--format <yt-dlp format string>` set the format of anime downloaded and streamed based on yt-dlp format. Works when `--server gogoanime`
|
- `--format <yt-dlp format string>` or `-f <yt-dlp format string>` set the format of anime downloaded and streamed based on yt-dlp format. Works when `--server gogoanime`
|
||||||
- `--icons/--no-icons` toggle the visibility of the icons
|
- `--icons/--no-icons` toggle the visibility of the icons
|
||||||
- `--skip/--no-skip` whether to skip the opening and ending theme songs.
|
- `--skip/--no-skip` whether to skip the opening and ending theme songs.
|
||||||
- `--rofi` use rofi for the ui
|
- `--rofi` use rofi for the ui
|
||||||
@@ -220,6 +222,19 @@ Available options include:
|
|||||||
- `--log-file` allow logging to a file
|
- `--log-file` allow logging to a file
|
||||||
- `--rich-traceback` allow rich traceback
|
- `--rich-traceback` allow rich traceback
|
||||||
- `--use-mpv-mod/--use-default-player` whether to use python-mpv
|
- `--use-mpv-mod/--use-default-player` whether to use python-mpv
|
||||||
|
- `--provider <allanime/animepahe>` anime site of choice to scrape from **NOTE:** animepahe is still experimental and requires node to decode one line of js thats hard to decode manually
|
||||||
|
|
||||||
|
Example usage of the above options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# downloading dubbed anime
|
||||||
|
fastanime --dub download <anime>
|
||||||
|
# use icons and fzf for a more elegant ui with preview
|
||||||
|
# only for anilist
|
||||||
|
fastanime --icons --preview --fzf anilist
|
||||||
|
# use icons with default ui
|
||||||
|
fastanime --icons --default anilist
|
||||||
|
```
|
||||||
|
|
||||||
#### The anilist command :fire: :fire: :fire:
|
#### The anilist command :fire: :fire: :fire:
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ if sys.version_info < (3, 10):
|
|||||||
) # noqa: F541
|
) # noqa: F541
|
||||||
|
|
||||||
|
|
||||||
__version__ = "v1.1.5"
|
__version__ = "v1.6.3"
|
||||||
|
|
||||||
APP_NAME = "FastAnime"
|
APP_NAME = "FastAnime"
|
||||||
AUTHOR = "Benex254"
|
AUTHOR = "Benex254"
|
||||||
|
|||||||
@@ -76,7 +76,14 @@ signal.signal(signal.SIGINT, handle_exit)
|
|||||||
@click.option(
|
@click.option(
|
||||||
"-q",
|
"-q",
|
||||||
"--quality",
|
"--quality",
|
||||||
type=click.Choice(["360", "720", "1080", "unknown"]),
|
type=click.Choice(
|
||||||
|
[
|
||||||
|
"360",
|
||||||
|
"480",
|
||||||
|
"720",
|
||||||
|
"1080",
|
||||||
|
]
|
||||||
|
),
|
||||||
help="set the quality of the stream",
|
help="set the quality of the stream",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
|
from ...utils.completion_types import anime_titles_shell_complete
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
help="Search for anime using anilists api and get top ~50 results",
|
help="Search for anime using anilists api and get top ~50 results",
|
||||||
short_help="Search for anime",
|
short_help="Search for anime",
|
||||||
)
|
)
|
||||||
@click.argument(
|
@click.argument("title", shell_complete=anime_titles_shell_complete)
|
||||||
"title",
|
|
||||||
)
|
|
||||||
@click.pass_obj
|
@click.pass_obj
|
||||||
def search(config, title):
|
def search(config, title):
|
||||||
from ....anilist import AniList
|
from ....anilist import AniList
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
|
from ..utils.completion_types import anime_titles_shell_complete
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
|
|
||||||
@@ -12,8 +14,7 @@ if TYPE_CHECKING:
|
|||||||
short_help="Download anime",
|
short_help="Download anime",
|
||||||
)
|
)
|
||||||
@click.argument(
|
@click.argument(
|
||||||
"anime-title",
|
"anime-title", required=True, shell_complete=anime_titles_shell_complete
|
||||||
required=True,
|
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--episode-range",
|
"--episode-range",
|
||||||
@@ -91,10 +92,12 @@ def download(config: "Config", anime_title, episode_range, highest_priority):
|
|||||||
episodes = anime["availableEpisodesDetail"][config.translation_type]
|
episodes = anime["availableEpisodesDetail"][config.translation_type]
|
||||||
if episode_range:
|
if episode_range:
|
||||||
episodes_start, episodes_end = episode_range.split("-")
|
episodes_start, episodes_end = episode_range.split("-")
|
||||||
|
episodes_range = range(round(float(episodes_start)), round(float(episodes_end)))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
episodes_start, episodes_end = 0, len(episodes)
|
episodes_range = sorted(episodes, key=float)
|
||||||
for episode in range(round(float(episodes_start)), round(float(episodes_end))):
|
|
||||||
|
for episode in episodes_range:
|
||||||
try:
|
try:
|
||||||
episode = str(episode)
|
episode = str(episode)
|
||||||
if episode not in episodes:
|
if episode not in episodes:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from ...cli.config import Config
|
from ...cli.config import Config
|
||||||
|
from ..utils.completion_types import anime_titles_shell_complete
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
@@ -12,7 +13,9 @@ from ...cli.config import Config
|
|||||||
"-r",
|
"-r",
|
||||||
help="A range of episodes to binge",
|
help="A range of episodes to binge",
|
||||||
)
|
)
|
||||||
@click.argument("anime_title", required=True, type=str)
|
@click.argument(
|
||||||
|
"anime_title", required=True, shell_complete=anime_titles_shell_complete
|
||||||
|
)
|
||||||
@click.pass_obj
|
@click.pass_obj
|
||||||
def search(config: Config, anime_title: str, episode_range: str):
|
def search(config: Config, anime_title: str, episode_range: str):
|
||||||
from click import clear
|
from click import clear
|
||||||
|
|||||||
@@ -532,16 +532,26 @@ def provider_anime_episode_servers_menu(
|
|||||||
# this will try to update the episode to be the next episode if delta has reached a specific threshhold
|
# this will try to update the episode to be the next episode if delta has reached a specific threshhold
|
||||||
# this update will only apply locally
|
# this update will only apply locally
|
||||||
# the remote(anilist) is only updated when its certain you are going to open the player
|
# the remote(anilist) is only updated when its certain you are going to open the player
|
||||||
|
available_episodes: list = sorted(
|
||||||
|
fastanime_runtime_state.provider_available_episodes, key=float
|
||||||
|
)
|
||||||
if stop_time == "0" or total_time == "0":
|
if stop_time == "0" or total_time == "0":
|
||||||
# increment the episode
|
# increment the episodes
|
||||||
episode = str(int(current_episode_number) + 1)
|
next_episode = available_episodes.index(current_episode_number) + 1
|
||||||
|
if next_episode >= len(available_episodes):
|
||||||
|
next_episode = len(available_episodes) - 1
|
||||||
|
episode = available_episodes[next_episode]
|
||||||
else:
|
else:
|
||||||
error = config.error * 60
|
error = config.error * 60
|
||||||
delta = calculate_time_delta(stop_time, total_time)
|
delta = calculate_time_delta(stop_time, total_time)
|
||||||
if delta.total_seconds() > error:
|
if delta.total_seconds() > error:
|
||||||
episode = current_episode_number
|
episode = current_episode_number
|
||||||
else:
|
else:
|
||||||
episode = str(int(current_episode_number) + 1)
|
# increment the episodes
|
||||||
|
next_episode = available_episodes.index(current_episode_number) + 1
|
||||||
|
if next_episode >= len(available_episodes):
|
||||||
|
next_episode = len(available_episodes) - 1
|
||||||
|
episode = available_episodes[next_episode]
|
||||||
stop_time = "0"
|
stop_time = "0"
|
||||||
total_time = "0"
|
total_time = "0"
|
||||||
|
|
||||||
@@ -672,8 +682,7 @@ def fetch_anime_episode(config, fastanime_runtime_state: "FastAnimeRuntimeState"
|
|||||||
else:
|
else:
|
||||||
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
|
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
|
||||||
exit(1)
|
exit(1)
|
||||||
fetch_anime_episode(config, fastanime_runtime_state)
|
return fetch_anime_episode(config, fastanime_runtime_state)
|
||||||
return
|
|
||||||
|
|
||||||
fastanime_runtime_state.provider_anime = provider_anime
|
fastanime_runtime_state.provider_anime = provider_anime
|
||||||
provider_anime_episodes_menu(config, fastanime_runtime_state)
|
provider_anime_episodes_menu(config, fastanime_runtime_state)
|
||||||
@@ -719,8 +728,7 @@ def anime_provider_search_results_menu(
|
|||||||
else:
|
else:
|
||||||
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
|
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
|
||||||
exit(1)
|
exit(1)
|
||||||
anime_provider_search_results_menu(config, fastanime_runtime_state)
|
return anime_provider_search_results_menu(config, fastanime_runtime_state)
|
||||||
return
|
|
||||||
|
|
||||||
provider_search_results = {
|
provider_search_results = {
|
||||||
anime["title"]: anime for anime in provider_search_results["results"]
|
anime["title"]: anime for anime in provider_search_results["results"]
|
||||||
@@ -1176,6 +1184,7 @@ def anilist_results_menu(
|
|||||||
anime["status"] == "RELEASING"
|
anime["status"] == "RELEASING"
|
||||||
and anime["nextAiringEpisode"]
|
and anime["nextAiringEpisode"]
|
||||||
and progress > 0
|
and progress > 0
|
||||||
|
and anime["mediaListEntry"]
|
||||||
):
|
):
|
||||||
last_aired_episode = anime["nextAiringEpisode"]["episode"] - 1
|
last_aired_episode = anime["nextAiringEpisode"]["episode"] - 1
|
||||||
if last_aired_episode - progress > 0:
|
if last_aired_episode - progress > 0:
|
||||||
|
|||||||
98
fastanime/cli/utils/completion_types.py
Normal file
98
fastanime/cli/utils/completion_types.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ...libs.anilist.types import AnilistDataSchema
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ANILIST_ENDPOINT = "https://graphql.anilist.co"
|
||||||
|
|
||||||
|
|
||||||
|
anime_title_query = """
|
||||||
|
query($query:String){
|
||||||
|
Page(perPage:50){
|
||||||
|
pageInfo{
|
||||||
|
total
|
||||||
|
currentPage
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
media(search:$query,type:ANIME){
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
title{
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_anime_titles(query: str, variables: dict = {}):
|
||||||
|
"""the abstraction over all none authenticated requests and that returns data of a similar type
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: the anilist query
|
||||||
|
variables: the anilist api variables
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
a boolean indicating success and none or an anilist object depending on success
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
ANILIST_ENDPOINT,
|
||||||
|
json={"query": query, "variables": variables},
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
anilist_data: AnilistDataSchema = response.json()
|
||||||
|
|
||||||
|
# ensuring you dont get blocked
|
||||||
|
if (
|
||||||
|
int(response.headers.get("X-RateLimit-Remaining", 0)) < 30
|
||||||
|
and not response.status_code == 500
|
||||||
|
):
|
||||||
|
print("Warning you are exceeding the allowed number of calls per minute")
|
||||||
|
logger.warning(
|
||||||
|
"You are exceeding the allowed number of calls per minute for the AniList api enforcing timeout"
|
||||||
|
)
|
||||||
|
print("Forced timeout will now be initiated")
|
||||||
|
import time
|
||||||
|
|
||||||
|
print("sleeping...")
|
||||||
|
time.sleep(1 * 60)
|
||||||
|
if response.status_code == 200:
|
||||||
|
eng_titles = [
|
||||||
|
anime["title"]["english"]
|
||||||
|
for anime in anilist_data["data"]["Page"]["media"]
|
||||||
|
if anime["title"]["english"]
|
||||||
|
]
|
||||||
|
romaji_titles = [
|
||||||
|
anime["title"]["romaji"]
|
||||||
|
for anime in anilist_data["data"]["Page"]["media"]
|
||||||
|
if anime["title"]["romaji"]
|
||||||
|
]
|
||||||
|
return [*eng_titles, *romaji_titles]
|
||||||
|
else:
|
||||||
|
return ["non 200 status code"]
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
logger.warning(
|
||||||
|
"Timeout has been exceeded this could mean anilist is down or you have lost internet connection"
|
||||||
|
)
|
||||||
|
return ["timeout exceeded"]
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
logger.warning(
|
||||||
|
"ConnectionError this could mean anilist is down or you have lost internet connection"
|
||||||
|
)
|
||||||
|
return ["connection error"]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Something unexpected occured {e}")
|
||||||
|
return ["unexpected error"]
|
||||||
|
|
||||||
|
|
||||||
|
def anime_titles_shell_complete(ctx, param, incomplete):
|
||||||
|
return [name for name in get_anime_titles(anime_title_query, {"query": incomplete})]
|
||||||
@@ -234,6 +234,7 @@ class MpvPlayer(object):
|
|||||||
@mpv_player.on_key_press("shift+t")
|
@mpv_player.on_key_press("shift+t")
|
||||||
def _toggle_translation_type():
|
def _toggle_translation_type():
|
||||||
translation_type = "sub" if config.translation_type == "dub" else "dub"
|
translation_type = "sub" if config.translation_type == "dub" else "dub"
|
||||||
|
mpv_player.show_text("Changing translation type...")
|
||||||
anime = anime_provider.get_anime(
|
anime = anime_provider.get_anime(
|
||||||
fastanime_runtime_state.provider_anime_search_result["id"],
|
fastanime_runtime_state.provider_anime_search_result["id"],
|
||||||
fastanime_runtime_state.selected_anime_anilist,
|
fastanime_runtime_state.selected_anime_anilist,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ BG_GREEN = "\033[48;2;120;233;12;m"
|
|||||||
GREEN = "\033[38;2;45;24;45;m"
|
GREEN = "\033[38;2;45;24;45;m"
|
||||||
|
|
||||||
|
|
||||||
def filter_by_quality(quality: str, stream_links: "list[EpisodeStream]"):
|
def filter_by_quality(quality: str, stream_links: "list[EpisodeStream]", default=True):
|
||||||
"""Helper function used to filter a list of EpisodeStream objects to one that has a corresponding quality
|
"""Helper function used to filter a list of EpisodeStream objects to one that has a corresponding quality
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -30,8 +30,25 @@ def filter_by_quality(quality: str, stream_links: "list[EpisodeStream]"):
|
|||||||
an EpisodeStream object or None incase the quality was not found
|
an EpisodeStream object or None incase the quality was not found
|
||||||
"""
|
"""
|
||||||
for stream_link in stream_links:
|
for stream_link in stream_links:
|
||||||
if stream_link["quality"] == quality:
|
q = float(quality)
|
||||||
|
Q = float(stream_link["quality"])
|
||||||
|
# some providers have inaccurate eg qualities 718 instead of 720
|
||||||
|
if Q < q + 80 and Q > q - 80:
|
||||||
return stream_link
|
return stream_link
|
||||||
|
else:
|
||||||
|
if stream_links and default:
|
||||||
|
from rich import print
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("[yellow bold]WARNING Qualities were:[/] ", stream_links)
|
||||||
|
print(
|
||||||
|
"[cyan bold]Using default of quality:[/] ",
|
||||||
|
stream_links[0]["quality"],
|
||||||
|
)
|
||||||
|
return stream_links[0]
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def format_bytes_to_human(num_of_bytes: float, suffix: str = "B"):
|
def format_bytes_to_human(num_of_bytes: float, suffix: str = "B"):
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ PLATFORM = system()
|
|||||||
|
|
||||||
# ---- app deps ----
|
# ---- app deps ----
|
||||||
APP_DIR = os.path.abspath(os.path.dirname(__file__))
|
APP_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
CONFIGS_DIR = os.path.join(APP_DIR, "configs")
|
|
||||||
ASSETS_DIR = os.path.join(APP_DIR, "assets")
|
ASSETS_DIR = os.path.join(APP_DIR, "assets")
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +23,6 @@ PREVIEW_IMAGE = os.path.join(ASSETS_DIR, "preview")
|
|||||||
# ----- user configs and data -----
|
# ----- user configs and data -----
|
||||||
|
|
||||||
S_PLATFORM = sys.platform
|
S_PLATFORM = sys.platform
|
||||||
|
|
||||||
if S_PLATFORM == "win32":
|
if S_PLATFORM == "win32":
|
||||||
# app data
|
# app data
|
||||||
app_data_dir_base = os.getenv("LOCALAPPDATA")
|
app_data_dir_base = os.getenv("LOCALAPPDATA")
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ $type:MediaType\
|
|||||||
search_query = (
|
search_query = (
|
||||||
"""
|
"""
|
||||||
query($query:String,%s){
|
query($query:String,%s){
|
||||||
Page(perPage:30,page:$page){
|
Page(perPage:50,page:$page){
|
||||||
pageInfo{
|
pageInfo{
|
||||||
total
|
total
|
||||||
currentPage
|
currentPage
|
||||||
|
|||||||
@@ -89,27 +89,28 @@ class AnimePaheApi(AnimeProvider):
|
|||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
if not data:
|
if not data:
|
||||||
data.update(response.json())
|
data.update(response.json())
|
||||||
if ep_data := response.json().get("data"):
|
else:
|
||||||
data["data"].extend(ep_data)
|
if ep_data := response.json().get("data"):
|
||||||
if data["next_page_url"]:
|
data["data"].extend(ep_data)
|
||||||
# TODO: Refine this
|
if response.json()["next_page_url"]:
|
||||||
time.sleep(
|
# TODO: Refine this
|
||||||
random.choice(
|
time.sleep(
|
||||||
[
|
random.choice(
|
||||||
0.25,
|
[
|
||||||
0.1,
|
0.25,
|
||||||
0.5,
|
0.1,
|
||||||
0.75,
|
0.5,
|
||||||
1,
|
0.75,
|
||||||
]
|
1,
|
||||||
)
|
]
|
||||||
)
|
|
||||||
page += 1
|
|
||||||
url = f"{ANIMEPAHE_ENDPOINT}m=release&id={session_id}&sort=episode_asc&page={page}"
|
|
||||||
_pages_loader(
|
|
||||||
url,
|
|
||||||
page,
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
page += 1
|
||||||
|
url = f"{ANIMEPAHE_ENDPOINT}m=release&id={session_id}&sort=episode_asc&page={page}"
|
||||||
|
_pages_loader(
|
||||||
|
url,
|
||||||
|
page,
|
||||||
|
)
|
||||||
|
|
||||||
_pages_loader(
|
_pages_loader(
|
||||||
url,
|
url,
|
||||||
@@ -119,7 +120,7 @@ class AnimePaheApi(AnimeProvider):
|
|||||||
if not data:
|
if not data:
|
||||||
return {}
|
return {}
|
||||||
self.anime = data # pyright:ignore
|
self.anime = data # pyright:ignore
|
||||||
episodes = list(map(str, range(data["total"])))
|
episodes = list(map(str, [episode["episode"] for episode in data["data"]]))
|
||||||
title = ""
|
title = ""
|
||||||
return {
|
return {
|
||||||
"id": session_id,
|
"id": session_id,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ hex_to_char = {
|
|||||||
|
|
||||||
|
|
||||||
def give_random_quality(links: list[dict]):
|
def give_random_quality(links: list[dict]):
|
||||||
qualities = cycle(["1080", "720", "360"])
|
qualities = cycle(["1080", "720", "480", "360"])
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{"link": link["link"], "quality": quality}
|
{"link": link["link"], "quality": quality}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "fastanime"
|
name = "fastanime"
|
||||||
version = "1.1.5.dev1"
|
version = "1.6.3.dev1"
|
||||||
description = "A browser anime site experience from the terminal"
|
description = "A browser anime site experience from the terminal"
|
||||||
authors = ["Benextempest <benextempest@gmail.com>"]
|
authors = ["Benextempest <benextempest@gmail.com>"]
|
||||||
license = "UNLICENSE"
|
license = "UNLICENSE"
|
||||||
|
|||||||
Reference in New Issue
Block a user