mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-12 15:50:01 -08:00
@@ -60,7 +60,7 @@ signal.signal(signal.SIGINT, handle_exit)
|
||||
fastanime --icons --default anilist
|
||||
\b
|
||||
# viewing manga
|
||||
fastanime --manga search -t <manga-title>
|
||||
fastanime --manga search -t <manga-title>
|
||||
""",
|
||||
)
|
||||
@click.version_option(__version__, "--version")
|
||||
@@ -142,7 +142,7 @@ signal.signal(signal.SIGINT, handle_exit)
|
||||
@click.option(
|
||||
"--normalize-titles/--no-normalize-titles",
|
||||
type=bool,
|
||||
help="whether to normalize anime and episode titls given by providers",
|
||||
help="whether to normalize anime and episode titles given by providers",
|
||||
)
|
||||
@click.option("-d", "--downloads-dir", type=click.Path(), help="Downloads location")
|
||||
@click.option("--fzf", is_flag=True, help="Use fzf for the ui")
|
||||
|
||||
@@ -19,7 +19,7 @@ def login(config: "Config", status, erase):
|
||||
if status:
|
||||
is_logged_in = True if config.user else False
|
||||
message = (
|
||||
"You are logged in :smile:" if is_logged_in else "You arent logged in :cry:"
|
||||
"You are logged in :smile:" if is_logged_in else "You aren't logged in :cry:"
|
||||
)
|
||||
print(message)
|
||||
print(config.user)
|
||||
|
||||
@@ -2,7 +2,7 @@ import click
|
||||
|
||||
|
||||
@click.command(
|
||||
help="Get random anime from anilist based on a range of anilist anime ids that are seected at random",
|
||||
help="Get random anime from anilist based on a range of anilist anime ids that are selected at random",
|
||||
short_help="View random anime",
|
||||
)
|
||||
@click.option(
|
||||
|
||||
@@ -2,7 +2,7 @@ import click
|
||||
|
||||
|
||||
@click.command(
|
||||
help="Fetch the 15 most anticipited anime", short_help="View upcoming anime"
|
||||
help="Fetch the 15 most anticipated anime", short_help="View upcoming anime"
|
||||
)
|
||||
@click.option(
|
||||
"--dump-json",
|
||||
|
||||
@@ -59,7 +59,7 @@ def get_anime_titles(query: str, variables: dict = {}):
|
||||
else:
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"Something unexpected occured {e}")
|
||||
logger.error(f"Something unexpected occurred {e}")
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -764,18 +764,22 @@ def provider_anime_episodes_menu(
|
||||
if not current_episode_number or current_episode_number not in available_episodes:
|
||||
choices = [*available_episodes, "Back"]
|
||||
preview = None
|
||||
if config.preview:
|
||||
from .utils import get_fzf_episode_preview
|
||||
|
||||
e = fastanime_runtime_state.selected_anime_anilist["episodes"]
|
||||
if e:
|
||||
eps = range(0, e + 1)
|
||||
else:
|
||||
eps = available_episodes
|
||||
preview = get_fzf_episode_preview(
|
||||
fastanime_runtime_state.selected_anime_anilist, eps
|
||||
)
|
||||
if config.use_fzf:
|
||||
if config.preview:
|
||||
from .utils import get_fzf_episode_preview
|
||||
|
||||
e = fastanime_runtime_state.selected_anime_anilist["episodes"]
|
||||
if e:
|
||||
eps = range(0, e + 1)
|
||||
else:
|
||||
eps = available_episodes
|
||||
preview = get_fzf_episode_preview(
|
||||
fastanime_runtime_state.selected_anime_anilist, eps
|
||||
)
|
||||
|
||||
if not preview:
|
||||
print("Failed to find bash executable which is necessary for preview with fzf.\nIf you are on Windows, please make sure Git is installed and available in PATH.")
|
||||
|
||||
current_episode_number = fzf.run(
|
||||
choices, prompt="Select Episode", header=anime_title, preview=preview
|
||||
)
|
||||
@@ -1498,6 +1502,9 @@ def anilist_results_menu(
|
||||
from .utils import get_fzf_anime_preview
|
||||
|
||||
preview = get_fzf_anime_preview(search_results, anime_data.keys())
|
||||
if not preview:
|
||||
print("Failed to find bash executable which is necessary for preview with fzf.\nIf you are on Windows, please make sure Git is installed and available in PATH.")
|
||||
|
||||
selected_anime_title = fzf.run(
|
||||
choices,
|
||||
prompt="Select Anime",
|
||||
|
||||
@@ -13,7 +13,7 @@ from ...constants import APP_CACHE_DIR, S_PLATFORM
|
||||
from ...libs.anilist.types import AnilistBaseMediaDataSchema
|
||||
from ...Utility import anilist_data_helper
|
||||
from ..utils.scripts import fzf_preview
|
||||
from ..utils.utils import get_true_fg
|
||||
from ..utils.utils import get_true_fg, which_bashlike
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -96,7 +96,7 @@ def write_search_results(
|
||||
titles: list[str],
|
||||
workers: int | None = None,
|
||||
):
|
||||
"""A helper function used by and run in a background thread by get_fzf_preview function inorder to get the actual preview data to be displayed by fzf
|
||||
"""A helper function used by and run in a background thread by get_fzf_preview function in order to get the actual preview data to be displayed by fzf
|
||||
|
||||
Args:
|
||||
anilist_results: the anilist results from an anilist action
|
||||
@@ -122,7 +122,7 @@ def write_search_results(
|
||||
# handle the text data
|
||||
template = f"""
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
@@ -130,7 +130,7 @@ def write_search_results(
|
||||
echo "{get_true_fg('Title(jp):',*HEADER_COLOR)} {(anime['title']['romaji'] or "").replace('"',SINGLE_QUOTE)}"
|
||||
echo "{get_true_fg('Title(eng):',*HEADER_COLOR)} {(anime['title']['english'] or "").replace('"',SINGLE_QUOTE)}"
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
@@ -141,7 +141,7 @@ def write_search_results(
|
||||
echo "{get_true_fg('Next Episode:',*HEADER_COLOR)} {anilist_data_helper.extract_next_airing_episode(anime['nextAiringEpisode']).replace('"',SINGLE_QUOTE)}"
|
||||
echo "{get_true_fg('Genres:',*HEADER_COLOR)} {anilist_data_helper.format_list_data_with_comma(anime['genres']).replace('"',SINGLE_QUOTE)}"
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
@@ -150,7 +150,7 @@ def write_search_results(
|
||||
echo "{get_true_fg('Start Date:',*HEADER_COLOR)} {anilist_data_helper.format_anilist_date_object(anime['startDate']).replace('"',SINGLE_QUOTE)}"
|
||||
echo "{get_true_fg('End Date:',*HEADER_COLOR)} {anilist_data_helper.format_anilist_date_object(anime['endDate']).replace('"',SINGLE_QUOTE)}"
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
@@ -158,7 +158,7 @@ def write_search_results(
|
||||
echo "{get_true_fg('Media List:',*HEADER_COLOR)} {mediaListName.replace('"',SINGLE_QUOTE)}"
|
||||
echo "{get_true_fg('Progress:',*HEADER_COLOR)} {progress}"
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
@@ -279,6 +279,9 @@ def get_fzf_episode_preview(
|
||||
titles (list[str]): sanitized titles of the anime; NOTE: its important that they are sanitized since they are used as the filenames of the images
|
||||
workers ([TODO:parameter]): Number of threads to use to download the images; defaults to as many as possible
|
||||
anilist_results: the anilist results from an anilist action
|
||||
|
||||
Returns:
|
||||
The fzf preview script to use or None if the bash is not found
|
||||
"""
|
||||
|
||||
# HEADER_COLOR = 215, 0, 95
|
||||
@@ -305,7 +308,7 @@ def get_fzf_episode_preview(
|
||||
template = textwrap.dedent(
|
||||
f"""
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
@@ -313,13 +316,13 @@ def get_fzf_episode_preview(
|
||||
echo "{get_true_fg('Anime Title(jp):',*HEADER_COLOR)} {(anilist_result['title']['romaji'] or '').replace('"',SINGLE_QUOTE)}"
|
||||
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
echo "{str(episode_title).replace('"',SINGLE_QUOTE)}"
|
||||
ll=2
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
while [ $ll -le $FZF_PREVIEW_COLUMNS ];do
|
||||
echo -n -e "{get_true_fg("─",*SEPARATOR_COLOR,bold=False)}"
|
||||
((ll++))
|
||||
done
|
||||
@@ -345,22 +348,26 @@ def get_fzf_episode_preview(
|
||||
background_worker.start()
|
||||
|
||||
# the preview script is in bash so making sure fzf doesnt use any other shell lang to process the preview script
|
||||
os.environ["SHELL"] = shutil.which("bash") or "bash"
|
||||
bash_path = which_bashlike()
|
||||
if not bash_path:
|
||||
return
|
||||
|
||||
os.environ["SHELL"] = bash_path
|
||||
if S_PLATFORM == "win32":
|
||||
preview = """
|
||||
%s
|
||||
title={}
|
||||
show_image_previews="%s"
|
||||
dim=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}
|
||||
if [ $show_image_previews = "true" ];then
|
||||
if [ -s "%s\\\\\\${title}.png" ]; then
|
||||
if [ $show_image_previews = "true" ];then
|
||||
if [ -s "%s\\\\\\${title}.png" ]; then
|
||||
if command -v "chafa">/dev/null;then
|
||||
chafa -s $dim "%s\\\\\\${title}.png"
|
||||
else
|
||||
echo please install chafa to enjoy image previews
|
||||
fi
|
||||
echo
|
||||
else
|
||||
echo
|
||||
else
|
||||
echo Loading...
|
||||
fi
|
||||
fi
|
||||
@@ -380,7 +387,7 @@ def get_fzf_episode_preview(
|
||||
title={}
|
||||
%s
|
||||
show_image_previews="%s"
|
||||
if [ $show_image_previews = "true" ];then
|
||||
if [ $show_image_previews = "true" ];then
|
||||
if [ -s %s/${title}.png ]; then fzf-preview %s/${title}.png
|
||||
else echo Loading...
|
||||
fi
|
||||
@@ -412,7 +419,7 @@ def get_fzf_anime_preview(
|
||||
anilist_results: the anilist results got from an anilist action
|
||||
|
||||
Returns:
|
||||
THe fzf preview script to use
|
||||
The fzf preview script to use or None if the bash is not found
|
||||
"""
|
||||
# ensure images and info exists
|
||||
|
||||
@@ -423,7 +430,12 @@ def get_fzf_anime_preview(
|
||||
background_worker.start()
|
||||
|
||||
# the preview script is in bash so making sure fzf doesnt use any other shell lang to process the preview script
|
||||
os.environ["SHELL"] = shutil.which("bash") or "bash"
|
||||
bash_path = which_bashlike()
|
||||
if not bash_path:
|
||||
return
|
||||
|
||||
os.environ["SHELL"] = bash_path
|
||||
|
||||
if S_PLATFORM == "win32":
|
||||
preview = """
|
||||
%s
|
||||
@@ -431,14 +443,14 @@ def get_fzf_anime_preview(
|
||||
show_image_previews="%s"
|
||||
dim=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}
|
||||
if [ $show_image_previews = "true" ];then
|
||||
if [ -s "%s\\\\\\${title}.png" ]; then
|
||||
if [ -s "%s\\\\\\${title}.png" ]; then
|
||||
if command -v "chafa">/dev/null;then
|
||||
chafa -s $dim "%s\\\\\\${title}.png"
|
||||
else
|
||||
echo please install chafa to enjoy image previews
|
||||
fi
|
||||
echo
|
||||
else
|
||||
echo
|
||||
else
|
||||
echo Loading...
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -5,7 +5,7 @@ import requests
|
||||
|
||||
|
||||
def print_img(url: str):
|
||||
"""helper funtion to print an image given its url
|
||||
"""helper function to print an image given its url
|
||||
|
||||
Args:
|
||||
url: [TODO:description]
|
||||
@@ -25,7 +25,7 @@ def print_img(url: str):
|
||||
return
|
||||
img_bytes = res.content
|
||||
"""
|
||||
Change made in call to chafa. Chafa dev dropped abilty
|
||||
Change made in call to chafa. Chafa dev dropped ability
|
||||
to pull from urls. Keeping old line here just in case.
|
||||
|
||||
subprocess.run([EXECUTABLE, url, "--size=15x15"], input=img_bytes)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import logging
|
||||
import shutil
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from InquirerPy import inquirer
|
||||
|
||||
from fastanime.constants import S_PLATFORM
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
if TYPE_CHECKING:
|
||||
from ...libs.anime_provider.types import EpisodeStream
|
||||
@@ -92,7 +95,7 @@ def filter_by_quality(quality: str, stream_links: "list[EpisodeStream]", default
|
||||
|
||||
|
||||
def format_bytes_to_human(num_of_bytes: float, suffix: str = "B"):
|
||||
"""Helper function usedd to format bytes to human
|
||||
"""Helper function used to format bytes to human
|
||||
|
||||
Args:
|
||||
num_of_bytes: the number of bytes to format
|
||||
@@ -155,3 +158,41 @@ def fuzzy_inquirer(choices: list, prompt: str, **kwargs):
|
||||
**kwargs,
|
||||
).execute()
|
||||
return action
|
||||
|
||||
|
||||
def which_win32_gitbash():
|
||||
"""Helper function that returns absolute path to the git bash executable
|
||||
(came with Git for Windows) on Windows
|
||||
|
||||
Returns:
|
||||
the path to the git bash executable or None if not found
|
||||
"""
|
||||
from os import path
|
||||
|
||||
gb_path = shutil.which("bash")
|
||||
|
||||
# Windows came with its own bash.exe but it's just an entry point for WSL not Git Bash
|
||||
if gb_path and not path.dirname(gb_path).lower().endswith("windows\\system32"):
|
||||
return gb_path
|
||||
|
||||
git_path = shutil.which("git")
|
||||
|
||||
if git_path:
|
||||
if path.dirname(git_path).endswith("cmd"):
|
||||
gb_path = path.abspath(
|
||||
path.join(path.dirname(git_path), "..", "bin", "bash.exe")
|
||||
)
|
||||
else:
|
||||
gb_path = path.join(path.dirname(git_path), "bash.exe")
|
||||
|
||||
if path.exists(gb_path):
|
||||
return gb_path
|
||||
|
||||
|
||||
def which_bashlike():
|
||||
"""Helper function that returns absolute path to the bash executable for the current platform
|
||||
|
||||
Returns:
|
||||
the path to the bash executable or None if not found
|
||||
"""
|
||||
return (shutil.which("bash") or "bash") if S_PLATFORM != "win32" else which_win32_gitbash()
|
||||
@@ -64,7 +64,7 @@ class AniListApi:
|
||||
self.session = requests.session()
|
||||
|
||||
def login_user(self, token: str):
|
||||
"""methosd used to login a new user enabling authenticated requests
|
||||
"""method used to login a new user enabling authenticated requests
|
||||
|
||||
Args:
|
||||
token: anilist app token
|
||||
@@ -247,7 +247,7 @@ class AniListApi:
|
||||
return (False, None)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Something unexpected occured {e}")
|
||||
logger.error(f"Something unexpected occurred {e}")
|
||||
return (False, None) # type: ignore
|
||||
|
||||
def get_data(
|
||||
@@ -311,7 +311,7 @@ class AniListApi:
|
||||
},
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
logger.error(f"Something unexpected occured {e}")
|
||||
logger.error(f"Something unexpected occurred {e}")
|
||||
return (False, {"Error": f"{e}"}) # type: ignore
|
||||
|
||||
def search(
|
||||
@@ -456,7 +456,7 @@ class AniListApi:
|
||||
recommended_anime = self.get_data(recommended_query, variables)
|
||||
return recommended_anime
|
||||
|
||||
def get_charcters_of(self, id: int, type="ANIME", *_, **kwargs):
|
||||
def get_characters_of(self, id: int, type="ANIME", *_, **kwargs):
|
||||
variables = {"id": id}
|
||||
characters = self.get_data(anime_characters_query, variables)
|
||||
return characters
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
This module contains all the preset queries for the sake of neatness and convinience
|
||||
This module contains all the preset queries for the sake of neatness and convenience
|
||||
Mostly for internal usage
|
||||
"""
|
||||
|
||||
@@ -26,7 +26,7 @@ query($id:Int){
|
||||
}
|
||||
}
|
||||
body
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ query{
|
||||
large
|
||||
medium
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
"""
|
||||
@@ -909,7 +909,7 @@ query ($id: Int,$type:MediaType) {
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ def get_mal_id_and_anilist_id(anime_title: str) -> "dict[str,int] | None":
|
||||
)
|
||||
return {"id_anilist": anime["id"], "id_mal": anime["idMal"]}
|
||||
except Exception as e:
|
||||
logger.error(f"Something unexpected occured {e}")
|
||||
logger.error(f"Something unexpected occurred {e}")
|
||||
|
||||
|
||||
def get_basic_anime_info_by_title(anime_title: str):
|
||||
@@ -320,4 +320,4 @@ def get_basic_anime_info_by_title(anime_title: str):
|
||||
],
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Something unexpected occured {e}")
|
||||
logger.error(f"Something unexpected occurred {e}")
|
||||
|
||||
@@ -8,10 +8,12 @@ from typing import Callable, List
|
||||
from click import clear
|
||||
from rich import print
|
||||
|
||||
from ...cli.utils.tools import exit_app
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
FZF_DEFAULT_OPTS = """
|
||||
FZF_DEFAULT_OPTS = """
|
||||
--color=fg:#d0d0d0,fg+:#d0d0d0,bg:#121212,bg+:#262626
|
||||
--color=hl:#5f87af,hl+:#5fd7ff,info:#afaf87,marker:#87ff00
|
||||
--color=prompt:#d7005f,spinner:#af5fff,pointer:#af5fff,header:#87afaf
|
||||
@@ -127,7 +129,10 @@ class FZF:
|
||||
encoding="utf-8",
|
||||
)
|
||||
if not result or result.returncode != 0 or not result.stdout:
|
||||
print("sth went wrong:confused:")
|
||||
if result.returncode == 130: # fzf terminated by ctrl-c
|
||||
exit_app()
|
||||
|
||||
print("sth went wrong :confused:")
|
||||
input("press enter to try again...")
|
||||
clear()
|
||||
return self._run_fzf(commands, _fzf_input)
|
||||
|
||||
Reference in New Issue
Block a user