mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-12 15:50:01 -08:00
feat(cli):improve anilist interfaces api
This commit is contained in:
@@ -30,6 +30,6 @@ def format_list_data_with_comma(data: list | None):
|
||||
|
||||
def extract_next_airing_episode(airing_episode: AnilistMediaNextAiringEpisode):
|
||||
if airing_episode:
|
||||
return f"Episode: {airing_episode['episode']} on {format_anilist_timestamp(airing_episode['airingAt'])}"
|
||||
return f"{airing_episode['episode']} on {format_anilist_timestamp(airing_episode['airingAt'])}"
|
||||
else:
|
||||
return "Completed"
|
||||
|
||||
@@ -7,6 +7,11 @@ from datetime import datetime
|
||||
# from .kivy_markup_helper import color_text
|
||||
|
||||
|
||||
def remove_html_tags(text):
|
||||
clean = re.compile("<.*?>")
|
||||
return re.sub(clean, "", text)
|
||||
|
||||
|
||||
# utility functions
|
||||
def write_crash(e: Exception):
|
||||
index = datetime.today()
|
||||
|
||||
@@ -36,6 +36,7 @@ if not APP_DATA_DIR:
|
||||
|
||||
USER_DATA_PATH = os.path.join(APP_DATA_DIR, "user_data.json")
|
||||
USER_CONFIG_PATH = os.path.join(APP_DATA_DIR, "config.ini")
|
||||
USER_WATCH_HISTORY = os.path.join(APP_DATA_DIR, "watch_history.json")
|
||||
|
||||
|
||||
# video dir
|
||||
|
||||
@@ -24,7 +24,7 @@ commands = {
|
||||
"--server",
|
||||
type=click.Choice(SERVERS_AVAILABLE, case_sensitive=False),
|
||||
)
|
||||
@click.option("-h", "--hist", type=bool)
|
||||
@click.option("-c-h/-no-h", "--continue_h/--no-continue_h", type=bool)
|
||||
@click.option("-q", "--quality", type=int)
|
||||
@click.option("-t-t", "--translation_type")
|
||||
@click.option("-a-n", "--auto-next", type=bool)
|
||||
@@ -38,7 +38,7 @@ commands = {
|
||||
def run_cli(
|
||||
ctx: click.Context,
|
||||
server,
|
||||
hist,
|
||||
continue_h,
|
||||
translation_type,
|
||||
quality,
|
||||
auto_next,
|
||||
@@ -48,8 +48,8 @@ def run_cli(
|
||||
ctx.obj = Config()
|
||||
if server:
|
||||
ctx.obj.server = server
|
||||
if hist:
|
||||
ctx.obj.continue_from_history = hist
|
||||
if continue_h:
|
||||
ctx.obj.continue_from_history = continue_h
|
||||
if quality:
|
||||
ctx.obj.quality = quality
|
||||
if auto_next:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import click
|
||||
|
||||
from ...interfaces import anilist as anilist_interface
|
||||
from ...interfaces.anilist_interfaces import anilist as anilist_interface
|
||||
from ...utils.tools import QueryDict
|
||||
from .favourites import favourites
|
||||
from .popular import popular
|
||||
from .recent import recent
|
||||
@@ -19,6 +20,8 @@ commands = {
|
||||
|
||||
|
||||
@click.group(commands=commands, invoke_without_command=True)
|
||||
@click.pass_obj
|
||||
def anilist(config):
|
||||
anilist_interface(config=config)
|
||||
@click.pass_context
|
||||
def anilist(ctx: click.Context):
|
||||
if ctx.invoked_subcommand is None:
|
||||
anilist_config = QueryDict()
|
||||
anilist_interface(ctx.obj, anilist_config)
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import click
|
||||
|
||||
from ....libs.anilist.anilist import AniList
|
||||
from ...interfaces.anilist_interfaces import select_anime
|
||||
from ...utils.tools import QueryDict
|
||||
|
||||
|
||||
@click.command()
|
||||
def favourites():
|
||||
print("favourites")
|
||||
@click.pass_obj
|
||||
def favourites(config):
|
||||
anime_data = AniList.get_most_favourite()
|
||||
if anime_data[0]:
|
||||
anilist_config = QueryDict()
|
||||
anilist_config.data = anime_data[1]
|
||||
select_anime(config, anilist_config)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import click
|
||||
|
||||
from ..interfaces import anime_provider_
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.pass_obj
|
||||
@@ -9,7 +7,4 @@ def search(
|
||||
config,
|
||||
anime_title,
|
||||
):
|
||||
anime_provider_(
|
||||
config,
|
||||
anime_title,
|
||||
)
|
||||
pass
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import json
|
||||
import os
|
||||
from configparser import ConfigParser
|
||||
|
||||
from .. import USER_CONFIG_PATH, USER_DOWNLOADS_DIR
|
||||
from .. import USER_CONFIG_PATH, USER_DOWNLOADS_DIR, USER_WATCH_HISTORY
|
||||
|
||||
|
||||
class Config(object):
|
||||
@@ -15,6 +16,7 @@ class Config(object):
|
||||
"sort_by": "search match",
|
||||
"downloads_dir": USER_DOWNLOADS_DIR,
|
||||
"translation_type": "sub",
|
||||
"preferred_language": "romaji",
|
||||
}
|
||||
)
|
||||
self.configparser.add_section("stream")
|
||||
@@ -33,10 +35,26 @@ class Config(object):
|
||||
self.auto_next = self.get_auto_next()
|
||||
self.quality = self.get_quality()
|
||||
self.server = self.get_server()
|
||||
self.preferred_language = self.get_preferred_language()
|
||||
|
||||
# ---- setup history ------
|
||||
if not os.path.exists(USER_WATCH_HISTORY):
|
||||
self.watch_history = {}
|
||||
else:
|
||||
with open(USER_WATCH_HISTORY, "r") as history:
|
||||
self.watch_history = json.load(history)
|
||||
|
||||
def update_watch_history(self, title, episode):
|
||||
with open(USER_WATCH_HISTORY, "w") as history:
|
||||
self.watch_history[title] = episode
|
||||
json.dump(self.watch_history, history)
|
||||
|
||||
def get_downloads_dir(self):
|
||||
return self.configparser.get("general", "downloads_dir")
|
||||
|
||||
def get_preferred_language(self):
|
||||
return self.configparser.get("general", "preferred_language")
|
||||
|
||||
def get_sort_by(self):
|
||||
return self.configparser.get("anilist", "sort_by")
|
||||
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from InquirerPy import inquirer
|
||||
from rich import print
|
||||
|
||||
from ..libs.anilist.anilist import AniList
|
||||
from ..libs.anilist.anilist_data_schema import AnilistDataSchema
|
||||
from ..libs.anime_provider.allanime.api import anime_provider
|
||||
from .config import Config
|
||||
from .utils.mpv import mpv
|
||||
from .utils.utils import clear, fuzzy_inquirer, get_selected_anime, get_selected_server
|
||||
|
||||
|
||||
def fetch_episode(config: Config, anime, translation_type, selected_anime):
|
||||
# fetch episode
|
||||
episode_number = fuzzy_inquirer(
|
||||
"Select Episode:",
|
||||
[*anime["show"]["availableEpisodesDetail"][translation_type], "back"],
|
||||
)
|
||||
if episode_number == "back":
|
||||
anime_provider_(
|
||||
config,
|
||||
selected_anime[0]["name"],
|
||||
)
|
||||
return
|
||||
episode = anime_provider.get_anime_episode(
|
||||
selected_anime[0]["_id"], episode_number, config.translation_type
|
||||
)
|
||||
|
||||
fetch_streams(config, episode, anime, translation_type, selected_anime)
|
||||
|
||||
|
||||
def fetch_streams(config: Config, episode, *args):
|
||||
episode_streams = list(anime_provider.get_episode_streams(episode))
|
||||
|
||||
server = fuzzy_inquirer(
|
||||
"Select Server:", [episode_stream[0] for episode_stream in episode_streams]
|
||||
)
|
||||
selected_server = get_selected_server(server, episode_streams)
|
||||
|
||||
quality = config.quality
|
||||
links = selected_server[1]["links"]
|
||||
if quality > len(links) - 1:
|
||||
quality = config.quality = len(links) - 1
|
||||
elif quality < 0:
|
||||
quality = config.quality = 0
|
||||
stream_link = links[quality]["link"]
|
||||
print(
|
||||
"[bold magenta]Now playing:[/]",
|
||||
args[-1][0]["name"],
|
||||
"[bold magenta] Episode: [/]",
|
||||
episode["episode"]["episodeString"],
|
||||
)
|
||||
mpv(stream_link)
|
||||
clear()
|
||||
player_controls(config, episode, links, *args)
|
||||
|
||||
|
||||
def player_controls(config: Config, episode, links: list, *args):
|
||||
anime = args[0]
|
||||
selected_anime = args[-1]
|
||||
episodes = [*anime["show"]["availableEpisodesDetail"][config.translation_type]]
|
||||
episodes = sorted(episodes, key=int)
|
||||
current_episode = episode["episode"]["episodeString"]
|
||||
|
||||
def _back():
|
||||
fetch_streams(config, episode, *args)
|
||||
|
||||
def _replay():
|
||||
stream_link = links[config.quality]["link"]
|
||||
print(
|
||||
"[bold magenta]Now playing:[/]",
|
||||
args[-1][0]["name"],
|
||||
"[bold magenta] Episode: [/]",
|
||||
episode["episode"]["episodeString"],
|
||||
)
|
||||
mpv(stream_link)
|
||||
clear()
|
||||
player_controls(config, episode, links, *args)
|
||||
|
||||
def _next_episode():
|
||||
next_episode = episodes.index(current_episode) + 1
|
||||
if next_episode >= len(episodes):
|
||||
next_episode = len(episodes) - 1
|
||||
episode = anime_provider.get_anime_episode(
|
||||
selected_anime[0]["_id"], episodes[next_episode], config.translation_type
|
||||
)
|
||||
|
||||
fetch_streams(config, episode, *args)
|
||||
|
||||
def _episodes():
|
||||
fetch_episode(config, *args)
|
||||
|
||||
def _previous_episode():
|
||||
prev_episode = episodes.index(current_episode) - 1
|
||||
if prev_episode <= 0:
|
||||
prev_episode = 0
|
||||
episode = anime_provider.get_anime_episode(
|
||||
selected_anime[0]["_id"], episodes[prev_episode], config.translation_type
|
||||
)
|
||||
|
||||
fetch_streams(config, episode, *args)
|
||||
|
||||
def _change_quality():
|
||||
options = [link["link"] for link in links]
|
||||
quality = fuzzy_inquirer("Select Quality:", options)
|
||||
config.quality = options.index(quality) # set quality
|
||||
player_controls(config, episode, links, *args)
|
||||
|
||||
def _change_translation_type():
|
||||
options = ["sub", "dub"]
|
||||
translation_type = fuzzy_inquirer("Select Translation Type:", options)
|
||||
config.translation_type = translation_type # set trannslation type
|
||||
player_controls(config, episode, links, *args)
|
||||
|
||||
options = {
|
||||
"Replay": _replay,
|
||||
"Next Episode": _next_episode,
|
||||
"Episodes": _episodes,
|
||||
"Previous Episode": _previous_episode,
|
||||
"Change Quality": _change_quality,
|
||||
"Change Translation Type": _change_translation_type,
|
||||
"Back": _back,
|
||||
}
|
||||
|
||||
action = fuzzy_inquirer("Select Action:", options.keys())
|
||||
options[action]()
|
||||
|
||||
|
||||
def anime_provider_(config: Config, anime_title, **kwargs):
|
||||
translation_type = config.translation_type
|
||||
search_results = anime_provider.search_for_anime(anime_title, translation_type)
|
||||
search_results_anime_titles = [
|
||||
anime["name"] for anime in search_results["shows"]["edges"]
|
||||
]
|
||||
selected_anime_title = fuzzy_inquirer(
|
||||
"Select Search Result:",
|
||||
[*search_results_anime_titles, "back"],
|
||||
default=kwargs.get("default_anime_title", ""),
|
||||
)
|
||||
if selected_anime_title == "back":
|
||||
anilist(config)
|
||||
return
|
||||
fetch_anime_epiosode(
|
||||
config,
|
||||
selected_anime_title,
|
||||
search_results,
|
||||
)
|
||||
|
||||
|
||||
def fetch_anime_epiosode(config, selected_anime_title, search_results):
|
||||
translation_type = config.translation_type
|
||||
selected_anime = get_selected_anime(selected_anime_title, search_results)
|
||||
anime = anime_provider.get_anime(selected_anime[0]["_id"])
|
||||
|
||||
fetch_episode(config, anime, translation_type, selected_anime)
|
||||
|
||||
|
||||
def _stream(config, anilist_data: AnilistDataSchema, preferred_lang="romaji"):
|
||||
anime_titles = [
|
||||
str(anime["title"][preferred_lang])
|
||||
for anime in anilist_data["data"]["Page"]["media"]
|
||||
]
|
||||
selected_anime_title = fuzzy_inquirer("Select Anime:", anime_titles)
|
||||
anime_provider_(
|
||||
config, selected_anime_title, default_anime_title=selected_anime_title
|
||||
)
|
||||
|
||||
|
||||
def anilist_options(config, anilist_data: AnilistDataSchema):
|
||||
def _watch_trailer():
|
||||
pass
|
||||
|
||||
def _add_to_list():
|
||||
pass
|
||||
|
||||
def _remove_from_list():
|
||||
pass
|
||||
|
||||
def _view_info():
|
||||
pass
|
||||
|
||||
options = {
|
||||
"stream": _stream,
|
||||
"watch trailer": _watch_trailer,
|
||||
"add to list": _add_to_list,
|
||||
"remove from list": _remove_from_list,
|
||||
"view info": _view_info,
|
||||
"back": anilist,
|
||||
}
|
||||
action = fuzzy_inquirer("Select Action:", options.keys())
|
||||
options[action](config, anilist_data)
|
||||
|
||||
|
||||
def anilist(config, *args, **kwargs):
|
||||
def _anilist_search():
|
||||
search_term = inquirer.text(
|
||||
"Search:", instruction="Enter anime to search for"
|
||||
).execute()
|
||||
|
||||
return AniList.search(query=search_term)
|
||||
|
||||
options = {
|
||||
"trending": AniList.get_trending,
|
||||
"search": _anilist_search,
|
||||
"most popular anime": AniList.get_most_popular,
|
||||
"most favourite anime": AniList.get_most_favourite,
|
||||
"most scored anime": AniList.get_most_scored,
|
||||
"upcoming anime": AniList.get_most_favourite,
|
||||
"recently updated anime": AniList.get_most_recently_updated,
|
||||
}
|
||||
action = fuzzy_inquirer("Select Action:", options.keys())
|
||||
anilist_data = options[action]()
|
||||
if anilist_data[0]:
|
||||
anilist_options(config, anilist_data[1])
|
||||
404
fastanime/cli/interfaces/anilist_interfaces.py
Normal file
404
fastanime/cli/interfaces/anilist_interfaces.py
Normal file
@@ -0,0 +1,404 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
from InquirerPy import inquirer
|
||||
from rich import print
|
||||
|
||||
from ...libs.anilist.anilist import AniList
|
||||
from ...libs.anilist.anilist_data_schema import AnilistBaseMediaDataSchema
|
||||
from ...libs.anime_provider.allanime.api import anime_provider
|
||||
from ...libs.anime_provider.allanime.data_types import AllAnimeEpisode, AllAnimeShow
|
||||
from ..config import Config
|
||||
from ..utils.mpv import mpv
|
||||
from ..utils.tools import QueryDict
|
||||
from ..utils.utils import clear, fuzzy_inquirer
|
||||
|
||||
|
||||
# FIXME: BACK brocken
|
||||
def player_controls(config: Config, anilist_config: QueryDict):
|
||||
# user config
|
||||
translation_type: str = config.translation_type
|
||||
|
||||
# internal config
|
||||
_anime: AllAnimeShow = anilist_config._anime
|
||||
current_episode: str = anilist_config.episode_number
|
||||
episodes: list = anilist_config.episodes
|
||||
links: list = anilist_config.current_stream_links
|
||||
current_link: str = anilist_config.current_stream_link
|
||||
anime_title: str = anilist_config.anime_title
|
||||
|
||||
def _back():
|
||||
fetch_streams(config, anilist_config)
|
||||
|
||||
def _replay():
|
||||
print(
|
||||
"[bold magenta]Now Replaying:[/]",
|
||||
anime_title,
|
||||
"[bold magenta] Episode: [/]",
|
||||
current_episode,
|
||||
)
|
||||
|
||||
config.update_watch_history(anime_title, current_episode)
|
||||
|
||||
mpv(current_link)
|
||||
clear()
|
||||
player_controls(config, anilist_config)
|
||||
|
||||
def _next_episode():
|
||||
next_episode = episodes.index(current_episode) + 1
|
||||
if next_episode >= len(episodes):
|
||||
next_episode = len(episodes) - 1
|
||||
episode = anime_provider.get_anime_episode(
|
||||
_anime["_id"], episodes[next_episode], translation_type
|
||||
)
|
||||
|
||||
# update internal config
|
||||
anilist_config.episode = episode
|
||||
anilist_config.episode_number = episodes[next_episode]
|
||||
|
||||
# update user config
|
||||
config.update_watch_history(anime_title, episodes[next_episode])
|
||||
|
||||
# call interface
|
||||
fetch_streams(config, anilist_config)
|
||||
|
||||
def _episodes():
|
||||
# reset watch_history
|
||||
config.update_watch_history(anime_title, None)
|
||||
|
||||
# call interface
|
||||
fetch_episode(config, anilist_config)
|
||||
|
||||
def _previous_episode():
|
||||
prev_episode = episodes.index(current_episode) - 1
|
||||
if prev_episode <= 0:
|
||||
prev_episode = 0
|
||||
episode = anime_provider.get_anime_episode(
|
||||
_anime["_id"], episodes[prev_episode], config.translation_type
|
||||
)
|
||||
|
||||
# update internal config
|
||||
anilist_config.episode = episode
|
||||
# anilist_config.episode_title = episode["title"]
|
||||
anilist_config.episode_number = episodes[prev_episode]
|
||||
|
||||
# update user config
|
||||
config.update_watch_history(anime_title, episodes[prev_episode])
|
||||
|
||||
# call interface
|
||||
fetch_streams(config, anilist_config)
|
||||
|
||||
def _change_quality():
|
||||
# extract the actual link urls
|
||||
options = [link["link"] for link in links]
|
||||
|
||||
# prompt for new quality
|
||||
quality = fuzzy_inquirer("Select Quality:", options)
|
||||
config.quality = options.index(quality) # set quality
|
||||
player_controls(config, anilist_config)
|
||||
|
||||
def _change_translation_type():
|
||||
# prompt for new translation type
|
||||
options = ["sub", "dub"]
|
||||
translation_type = fuzzy_inquirer("Select Translation Type:", options)
|
||||
|
||||
# update internal config
|
||||
config.translation_type = translation_type
|
||||
|
||||
# reload to controls
|
||||
player_controls(config, anilist_config)
|
||||
|
||||
options = {
|
||||
"Replay": _replay,
|
||||
"Next Episode": _next_episode,
|
||||
"Episodes": _episodes,
|
||||
"Previous Episode": _previous_episode,
|
||||
"Change Quality": _change_quality,
|
||||
"Change Translation Type": _change_translation_type,
|
||||
"Back to servers": _back,
|
||||
"Go to Main Menu": lambda: anilist(config, anilist_config),
|
||||
"Go to Anime Options Menu": lambda: anilist_options(config, anilist_config),
|
||||
"Go to Search Results": lambda: select_anime(config, anilist_config),
|
||||
"exit": sys.exit,
|
||||
}
|
||||
|
||||
action = fuzzy_inquirer("Select Action:", options.keys())
|
||||
|
||||
# update_watch_history
|
||||
config.update_watch_history(anime_title, current_episode)
|
||||
options[action]()
|
||||
|
||||
|
||||
def fetch_streams(config: Config, anilist_config: QueryDict):
|
||||
# user config
|
||||
quality: int = config.quality
|
||||
|
||||
# internal config
|
||||
episode: AllAnimeEpisode = anilist_config.episode
|
||||
episode_number: str = anilist_config.episode_number
|
||||
anime_title: str = anilist_config.anime_title
|
||||
|
||||
# get streams for episode from provider
|
||||
episode_streams = anime_provider.get_episode_streams(episode)
|
||||
episode_streams = {
|
||||
episode_stream[0]: episode_stream[1] for episode_stream in episode_streams
|
||||
}
|
||||
|
||||
# prompt for preferred server
|
||||
server = fuzzy_inquirer("Select Server:", [*episode_streams.keys(), "back"])
|
||||
if server == "back":
|
||||
# reset watch_history
|
||||
config.update_watch_history(anime_title, None)
|
||||
|
||||
fetch_episode(config, anilist_config)
|
||||
return
|
||||
selected_server = episode_streams[server]
|
||||
|
||||
links = selected_server["links"]
|
||||
if quality > len(links) - 1:
|
||||
quality = config.quality = len(links) - 1
|
||||
elif quality < 0:
|
||||
quality = config.quality = 0
|
||||
stream_link = links[quality]["link"]
|
||||
|
||||
# update internal config
|
||||
anilist_config.current_stream_links = links
|
||||
anilist_config.current_stream_link = stream_link
|
||||
anilist_config.current_server = selected_server
|
||||
anilist_config.current_server_name = server
|
||||
|
||||
# play video
|
||||
print(
|
||||
"[bold magenta]Now playing:[/]",
|
||||
anime_title,
|
||||
"[bold magenta] Episode: [/]",
|
||||
episode_number,
|
||||
)
|
||||
|
||||
mpv(stream_link)
|
||||
|
||||
# switch to controls
|
||||
clear()
|
||||
player_controls(config, anilist_config)
|
||||
|
||||
|
||||
def fetch_episode(config: Config, anilist_config: QueryDict):
|
||||
# user config
|
||||
translation_type: str = config.translation_type
|
||||
continue_from_history: bool = config.continue_from_history
|
||||
user_watch_history = config.watch_history
|
||||
anime_title = anilist_config.anime_title
|
||||
|
||||
# internal config
|
||||
anime = anilist_config.anime
|
||||
_anime: AllAnimeShow = anilist_config._anime
|
||||
|
||||
# prompt for episode number
|
||||
# TODO: Load episode number from cache
|
||||
episodes = anime["show"]["availableEpisodesDetail"][translation_type]
|
||||
if continue_from_history and user_watch_history.get(anime_title) in episodes:
|
||||
episode_number = user_watch_history[anime_title]
|
||||
print(f"[bold cyan]Continuing from Episode:[/] [bold]{episode_number}[/]")
|
||||
else:
|
||||
episode_number = fuzzy_inquirer(
|
||||
"Select Episode:",
|
||||
[*episodes, "back"],
|
||||
)
|
||||
|
||||
if episode_number == "back":
|
||||
provide_anime(config, anilist_config)
|
||||
return
|
||||
config.update_watch_history(anime_title, episode_number)
|
||||
|
||||
# get the episode info from provider
|
||||
episode = anime_provider.get_anime_episode(
|
||||
_anime["_id"], episode_number, translation_type
|
||||
)
|
||||
|
||||
# update internal config
|
||||
anilist_config.episodes = episodes
|
||||
anilist_config.episode = episode
|
||||
# anilist_config.episode_title = episode["title"]
|
||||
anilist_config.episode_number = episode_number
|
||||
|
||||
# next interface
|
||||
fetch_streams(config, anilist_config)
|
||||
|
||||
|
||||
def fetch_anime_epiosode(config, anilist_config: QueryDict):
|
||||
selected_anime: AllAnimeShow = anilist_config._anime
|
||||
anilist_config.anime = anime_provider.get_anime(selected_anime["_id"])
|
||||
|
||||
fetch_episode(config, anilist_config)
|
||||
|
||||
|
||||
def provide_anime(config: Config, anilist_config: QueryDict):
|
||||
# user config
|
||||
translation_type = config.translation_type
|
||||
|
||||
# internal config
|
||||
selected_anime_title = anilist_config.selected_anime_title
|
||||
|
||||
# search and get the requested title from provider
|
||||
search_results = anime_provider.search_for_anime(
|
||||
selected_anime_title, translation_type
|
||||
)
|
||||
|
||||
search_results = {
|
||||
anime["name"]: anime for anime in search_results["shows"]["edges"]
|
||||
}
|
||||
anime_title = fuzzy_inquirer(
|
||||
"Select Search Result:",
|
||||
[*search_results.keys(), "back"],
|
||||
default=selected_anime_title,
|
||||
)
|
||||
|
||||
if anime_title == "back":
|
||||
anilist_options(config, anilist_config)
|
||||
return
|
||||
anilist_config.anime_title = anime_title
|
||||
anilist_config._anime = search_results[anime_title]
|
||||
fetch_anime_epiosode(config, anilist_config)
|
||||
|
||||
|
||||
def anilist_options(config, anilist_config: QueryDict):
|
||||
selected_anime: AnilistBaseMediaDataSchema = anilist_config.selected_anime_anilist
|
||||
selected_anime_title: str = anilist_config.selected_anime_title
|
||||
|
||||
def _watch_trailer(config, anilist_config):
|
||||
if trailer := selected_anime.get("trailer"):
|
||||
trailer_url = "https://youtube.com/watch?v=" + trailer["id"]
|
||||
print("[bold magenta]Watching Trailer of:[/]", selected_anime_title)
|
||||
mpv(trailer_url)
|
||||
anilist_options(config, anilist_config)
|
||||
|
||||
def _add_to_list(config, anilist_config):
|
||||
pass
|
||||
|
||||
def _remove_from_list():
|
||||
pass
|
||||
|
||||
def _change_translation_type(config, anilist_config):
|
||||
# prompt for new translation type
|
||||
options = ["sub", "dub"]
|
||||
translation_type = fuzzy_inquirer("Select Translation Type:", options)
|
||||
|
||||
# update internal config
|
||||
config.translation_type = translation_type
|
||||
|
||||
anilist_options(config, anilist_config)
|
||||
|
||||
def _view_info(config, anilist_config):
|
||||
from InquirerPy import inquirer
|
||||
from rich.console import Console
|
||||
|
||||
from ...Utility import anilist_data_helper
|
||||
from ...Utility.utils import remove_html_tags
|
||||
from ..utils.print_img import print_img
|
||||
|
||||
clear()
|
||||
console = Console()
|
||||
|
||||
print_img(selected_anime["coverImage"]["medium"])
|
||||
console.print("[bold cyan]Title(jp): ", selected_anime["title"]["romaji"])
|
||||
console.print("[bold cyan]Title(eng): ", selected_anime["title"]["english"])
|
||||
console.print("[bold cyan]Popularity: ", selected_anime["popularity"])
|
||||
console.print("[bold cyan]Favourites: ", selected_anime["favourites"])
|
||||
console.print("[bold cyan]Status: ", selected_anime["status"])
|
||||
console.print(
|
||||
"[bold cyan]Start Date: ",
|
||||
anilist_data_helper.format_anilist_date_object(selected_anime["startDate"]),
|
||||
)
|
||||
console.print(
|
||||
"[bold cyan]End Date: ",
|
||||
anilist_data_helper.format_anilist_date_object(selected_anime["endDate"]),
|
||||
)
|
||||
# console.print("[bold cyan]Season: ", selected_anime["season"])
|
||||
console.print("[bold cyan]Episodes: ", selected_anime["episodes"])
|
||||
console.print(
|
||||
"[bold cyan]Tags: ",
|
||||
anilist_data_helper.format_list_data_with_comma(
|
||||
[tag["name"] for tag in selected_anime["tags"]]
|
||||
),
|
||||
)
|
||||
console.print(
|
||||
"[bold cyan]Genres: ",
|
||||
anilist_data_helper.format_list_data_with_comma(selected_anime["genres"]),
|
||||
)
|
||||
# console.print("[bold cyan]Type: ", selected_anime["st"])
|
||||
if selected_anime["nextAiringEpisode"]:
|
||||
console.print(
|
||||
"[bold cyan]Next Episode: ",
|
||||
anilist_data_helper.extract_next_airing_episode(
|
||||
selected_anime["nextAiringEpisode"]
|
||||
),
|
||||
)
|
||||
console.print(
|
||||
"[bold underline cyan]Description\n[/]",
|
||||
remove_html_tags(str(selected_anime["description"])),
|
||||
)
|
||||
if inquirer.confirm("Enter to continue", default=True).execute():
|
||||
anilist_options(config, anilist_config)
|
||||
return
|
||||
|
||||
options = {
|
||||
"stream": provide_anime,
|
||||
"watch trailer": _watch_trailer,
|
||||
"add to list": _add_to_list,
|
||||
"remove from list": _remove_from_list,
|
||||
"view info": _view_info,
|
||||
"Change Translation Type": _change_translation_type,
|
||||
"back": select_anime,
|
||||
}
|
||||
action = fuzzy_inquirer("Select Action:", options.keys())
|
||||
options[action](config, anilist_config)
|
||||
|
||||
|
||||
def select_anime(config: Config, anilist_config: QueryDict):
|
||||
anime_data = {
|
||||
str(
|
||||
anime["title"][config.preferred_language] or anime["title"]["romaji"]
|
||||
): anime
|
||||
for anime in anilist_config.data["data"]["Page"]["media"]
|
||||
}
|
||||
selected_anime_title = fuzzy_inquirer(
|
||||
"Select Anime:",
|
||||
[
|
||||
*anime_data.keys(),
|
||||
"back",
|
||||
],
|
||||
)
|
||||
if selected_anime_title == "back":
|
||||
anilist(config, anilist_config)
|
||||
return
|
||||
|
||||
selected_anime = anime_data[selected_anime_title]
|
||||
anilist_config.selected_anime_anilist = selected_anime
|
||||
anilist_config.selected_anime_title = selected_anime_title
|
||||
|
||||
anilist_options(config, anilist_config)
|
||||
|
||||
|
||||
def anilist(config: Config, anilist_config: QueryDict):
|
||||
def _anilist_search():
|
||||
search_term = inquirer.text(
|
||||
"Search:", instruction="Enter anime to search for"
|
||||
).execute()
|
||||
|
||||
return AniList.search(query=search_term)
|
||||
|
||||
options = {
|
||||
"trending": AniList.get_trending,
|
||||
"search": _anilist_search,
|
||||
"most popular anime": AniList.get_most_popular,
|
||||
"most favourite anime": AniList.get_most_favourite,
|
||||
"most scored anime": AniList.get_most_scored,
|
||||
"upcoming anime": AniList.get_upcoming_anime,
|
||||
"recently updated anime": AniList.get_most_recently_updated,
|
||||
}
|
||||
action = fuzzy_inquirer("Select Action:", options.keys())
|
||||
anilist_data = options[action]()
|
||||
if anilist_data[0]:
|
||||
anilist_config.data = anilist_data[1]
|
||||
select_anime(config, anilist_config)
|
||||
24
fastanime/cli/utils/print_img.py
Normal file
24
fastanime/cli/utils/print_img.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
def print_img(url: str):
|
||||
executable = shutil.which("chafa")
|
||||
curl = shutil.which("curl")
|
||||
# curl -sL "$1" | chafa /dev/stdin
|
||||
|
||||
if executable is None or curl is None:
|
||||
print("chafa or curl not found")
|
||||
return
|
||||
|
||||
res = requests.get(url)
|
||||
if res.status_code != 200:
|
||||
print("Error fetching image")
|
||||
return
|
||||
img_bytes = res.content
|
||||
if not img_bytes:
|
||||
print("No image found")
|
||||
img_bytes = subprocess.check_output([curl, "-sL", url])
|
||||
subprocess.run([executable, url, "--size=15x15"], input=img_bytes)
|
||||
13
fastanime/cli/utils/tools.py
Normal file
13
fastanime/cli/utils/tools.py
Normal file
@@ -0,0 +1,13 @@
|
||||
class QueryDict(dict):
|
||||
"""dot.notation access to dictionary attributes"""
|
||||
|
||||
def __getattr__(self, attr):
|
||||
try:
|
||||
return self.__getitem__(attr)
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
"%r object has no attribute %r" % (self.__class__.__name__, attr)
|
||||
)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
self.__setitem__(attr, value)
|
||||
@@ -20,7 +20,12 @@ def clear():
|
||||
def fuzzy_inquirer(prompt: str, choices, **kwargs):
|
||||
clear()
|
||||
action = inquirer.fuzzy(
|
||||
prompt, choices, height="100%", border=True, **kwargs
|
||||
prompt,
|
||||
choices,
|
||||
height="100%",
|
||||
border=True,
|
||||
validate=lambda result: result in choices,
|
||||
**kwargs,
|
||||
).execute()
|
||||
return action
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ class AniList:
|
||||
return airing_schedule
|
||||
|
||||
@classmethod
|
||||
def get_upcoming_anime(cls, page: int, *_, **kwargs):
|
||||
def get_upcoming_anime(cls, page: int = 1, *_, **kwargs):
|
||||
"""
|
||||
Gets upcoming anime from anilist
|
||||
"""
|
||||
|
||||
@@ -11,7 +11,6 @@ from .constants import (
|
||||
ALLANIME_REFERER,
|
||||
USER_AGENT,
|
||||
)
|
||||
from .data_types import AllAnimeEpisode, AllAnimeSearchResults
|
||||
from .gql_queries import ALLANIME_EPISODES_GQL, ALLANIME_SEARCH_GQL, ALLANIME_SHOW_GQL
|
||||
from .utils import decode_hex_string
|
||||
|
||||
@@ -27,7 +26,7 @@ class AllAnimeAPI:
|
||||
|
||||
api_endpoint = ALLANIME_API_ENDPOINT
|
||||
|
||||
def _fetch_gql(self, query: str, variables: dict) -> dict:
|
||||
def _fetch_gql(self, query: str, variables: dict):
|
||||
try:
|
||||
response = requests.get(
|
||||
self.api_endpoint,
|
||||
@@ -38,16 +37,12 @@ class AllAnimeAPI:
|
||||
headers={"Referer": ALLANIME_REFERER, "User-Agent": USER_AGENT},
|
||||
timeout=10,
|
||||
)
|
||||
if response.status_code != 200:
|
||||
return {}
|
||||
return response.json().get("data", {})
|
||||
return response.json()["data"]
|
||||
except Exception as e:
|
||||
Logger.error(f"allanime:Error: {e}")
|
||||
return {}
|
||||
|
||||
def search_for_anime(
|
||||
self, user_query: str, translation_type: str = "sub"
|
||||
) -> AllAnimeSearchResults | dict:
|
||||
def search_for_anime(self, user_query: str, translation_type: str = "sub"):
|
||||
search = {"allowAdult": False, "allowUnknown": False, "query": user_query}
|
||||
limit = 40
|
||||
translationtype = translation_type
|
||||
@@ -75,7 +70,7 @@ class AllAnimeAPI:
|
||||
|
||||
def get_anime_episode(
|
||||
self, allanime_show_id: str, episode_string: str, translation_type: str = "sub"
|
||||
) -> AllAnimeEpisode | dict:
|
||||
):
|
||||
variables = {
|
||||
"showId": allanime_show_id,
|
||||
"translationType": translation_type,
|
||||
|
||||
@@ -15,7 +15,7 @@ fuzzywuzzy = "^0.18.0"
|
||||
rich = "^13.7.1"
|
||||
click = "^8.1.7"
|
||||
python-levenshtein = "^0.25.1"
|
||||
kivymd = [{url = "https://github.com/kivymd/KivyMD/archive/master.zip"}]
|
||||
kivymd = [{ url = "https://github.com/kivymd/KivyMD/archive/master.zip" }]
|
||||
|
||||
pyshortcuts = "^1.9.0"
|
||||
inquirerpy = "^0.3.4"
|
||||
@@ -40,4 +40,4 @@ fastanime = 'fastanime:FastAnime'
|
||||
[tool.bandit]
|
||||
#exclude = tests,path/to/file
|
||||
#tests = B201,B301
|
||||
skips = ["B311","B603","B607","B404"]
|
||||
skips = ["B311", "B603", "B607", "B404"]
|
||||
|
||||
Reference in New Issue
Block a user