feat(interface): improve rofi experience

This commit is contained in:
Benex254
2024-08-05 09:47:05 +03:00
parent a35c3276cf
commit 93a6d6cfc9
5 changed files with 233 additions and 42 deletions

View File

@@ -114,6 +114,16 @@ signal.signal(signal.SIGINT, handle_exit)
@click.option("--sub", help="Set the translation type to sub", is_flag=True)
@click.option("--rofi", help="Use rofi for the ui", is_flag=True)
@click.option("--rofi-theme", help="Rofi theme to use", type=click.Path())
@click.option(
"--rofi-theme-confirm",
help="Rofi theme to use for the confirm prompt",
type=click.Path(),
)
@click.option(
"--rofi-theme-input",
help="Rofi theme to use for the user input prompt",
type=click.Path(),
)
@click.pass_context
def run_cli(
ctx: click.Context,
@@ -137,6 +147,8 @@ def run_cli(
sub,
rofi,
rofi_theme,
rofi_theme_confirm,
rofi_theme_input,
):
ctx.obj = Config()
if provider:
@@ -183,8 +195,17 @@ def run_cli(
if rofi:
ctx.obj.use_fzf = False
ctx.obj.use_rofi = True
if rofi_theme:
ctx.obj.rofi_theme = rofi_theme
if rofi:
from ..libs.rofi import Rofi
Rofi.rofi_theme = rofi_theme
if rofi_theme:
ctx.obj.rofi_theme = rofi_theme
Rofi.rofi_theme = rofi_theme
if rofi_theme_input:
ctx.obj.rofi_theme_input = rofi_theme_input
Rofi.rofi_theme_input = rofi_theme_input
if rofi_theme_confirm:
ctx.obj.rofi_theme_confirm = rofi_theme_confirm
Rofi.rofi_theme_confirm = rofi_theme_confirm

View File

@@ -41,6 +41,8 @@ class Config(object):
"skip": "false",
"use_rofi": "false",
"rofi_theme": "",
"rofi_theme_input": "",
"rofi_theme_confirm": "",
}
)
self.configparser.add_section("stream")
@@ -73,7 +75,10 @@ class Config(object):
self.preferred_language = self.get_preferred_language()
self.rofi_theme = self.get_rofi_theme()
Rofi.rofi_theme = self.rofi_theme
self.rofi_theme_input = self.get_rofi_theme_input()
Rofi.rofi_theme_input = self.rofi_theme_input
self.rofi_theme_confirm = self.get_rofi_theme_confirm()
Rofi.rofi_theme_confirm = self.rofi_theme_confirm
# ---- setup user data ------
self.watch_history: dict = user_data_helper.user_data.get("watch_history", {})
self.anime_list: list = user_data_helper.user_data.get("animelist", [])
@@ -118,6 +123,12 @@ class Config(object):
def get_rofi_theme(self):
return self.configparser.get("general", "rofi_theme")
def get_rofi_theme_input(self):
return self.configparser.get("general", "rofi_theme_input")
def get_rofi_theme_confirm(self):
return self.configparser.get("general", "rofi_theme_confirm")
def get_downloads_dir(self):
return self.configparser.get("general", "downloads_dir")

View File

@@ -108,12 +108,19 @@ def player_controls(config: Config, anilist_config: QueryDict):
dt = delta.total_seconds()
if dt > error:
if config.auto_next:
if not Confirm.ask(
"Are you sure you wish to continue to the next episode you haven't completed the current episode?",
default=False,
):
anilist_options(config, anilist_config)
return
if config.use_rofi:
if not Rofi.confirm(
"Are you sure you wish to continue to the next episode you haven't completed the current episode?"
):
anilist_options(config, anilist_config)
return
else:
if not Confirm.ask(
"Are you sure you wish to continue to the next episode you haven't completed the current episode?",
default=False,
):
anilist_options(config, anilist_config)
return
elif not config.use_rofi:
if not Confirm.ask(
"Are you sure you wish to continue to the next episode, your progress for the current episodes will be erased?",
@@ -245,8 +252,12 @@ def fetch_streams(config: Config, anilist_config: QueryDict):
anime, episode_number, translation_type
)
if not episode_streams:
print("Failed to fetch :cry:")
input("Enter to retry...")
if not config.use_rofi:
print("Failed to fetch :cry:")
input("Enter to retry...")
else:
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
exit(1)
return fetch_streams(config, anilist_config)
episode_streams = {
@@ -406,11 +417,14 @@ def fetch_anime_episode(config, anilist_config: QueryDict):
progress.add_task("Fetching Anime Info...", total=None)
anilist_config.anime = anime_provider.get_anime(selected_anime["id"])
if not anilist_config.anime:
print(
"Sth went wrong :cry: this could mean the provider is down or your internet"
)
input("Enter to continue...")
if not config.use_rofi:
input("Enter to continue...")
else:
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
exit(1)
fetch_anime_episode(config, anilist_config)
return
@@ -437,7 +451,11 @@ def provide_anime(config: Config, anilist_config: QueryDict):
print(
"Sth went wrong :cry: while fetching this could mean you have poor internet connection or the provider is down"
)
input("Enter to continue...")
if not config.use_rofi:
input("Enter to continue...")
else:
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
exit(1)
provide_anime(config, anilist_config)
return
@@ -494,8 +512,12 @@ def anilist_options(config, anilist_config: QueryDict):
)
anilist_options(config, anilist_config)
else:
print("no trailer available :confused:")
input("Enter to continue...")
if not config.use_rofi:
print("no trailer available :confused:")
input("Enter to continue...")
else:
if not Rofi.confirm("No trailler found!!Enter to continue"):
exit(0)
anilist_options(config, anilist_config)
def _add_to_list(config: Config, anilist_config: QueryDict):
@@ -531,16 +553,21 @@ def anilist_options(config, anilist_config: QueryDict):
print(
f"Successfully added {selected_anime_title} to your {anime_list} list :smile:"
)
input("Enter to continue...")
if not config.use_rofi:
input("Enter to continue...")
anilist_options(config, anilist_config)
def _score_anime(config: Config, anilist_config: QueryDict):
score = inquirer.number(
message="Enter the score:",
min_allowed=0,
max_allowed=100,
validate=EmptyInputValidator(),
).execute()
if config.use_rofi:
score = Rofi.ask("Enter Score", is_int=True)
score = max(100, min(0, score))
else:
score = inquirer.number(
message="Enter the score:",
min_allowed=0,
max_allowed=100,
validate=EmptyInputValidator(),
).execute()
result = AniList.update_anime_list(
{"scoreRaw": score, "mediaId": selected_anime["id"]}
@@ -549,7 +576,8 @@ def anilist_options(config, anilist_config: QueryDict):
print("Failed to update", result)
else:
print(f"Successfully scored {selected_anime_title}; score: {score}")
input("Enter to continue...")
if not config.use_rofi:
input("Enter to continue...")
anilist_options(config, anilist_config)
def _remove_from_list(config: Config, anilist_config: QueryDict):
@@ -566,7 +594,8 @@ def anilist_options(config, anilist_config: QueryDict):
print("Successfully deleted :cry:", selected_anime_title)
else:
print(selected_anime_title, ":relieved:")
input("Enter to continue...")
if not config.use_rofi:
input("Enter to continue...")
anilist_options(config, anilist_config)
def _change_translation_type(config: Config, anilist_config: QueryDict):
@@ -639,6 +668,14 @@ def anilist_options(config, anilist_config: QueryDict):
anilist_options(config, anilist_config)
return
def _toggle_auto_select(config, anilist_config):
config.auto_select = not config.auto_select
anilist_options(config, anilist_config)
def _toggle_auto_next(config, anilist_config):
config.auto_select = not config.auto_select
anilist_options(config, anilist_config)
icons = config.icons
options = {
f"{'📽️ ' if icons else ''}Stream": provide_anime,
@@ -648,6 +685,8 @@ def anilist_options(config, anilist_config: QueryDict):
f"{'📤 ' if icons else ''}Remove from List": _remove_from_list,
f"{'📖 ' if icons else ''}View Info": _view_info,
f"{'🎧 ' if icons else ''}Change Translation Type": _change_translation_type,
f"{'🔘 ' if icons else ''}Toggle auto select anime": _toggle_auto_select, # problematic if you choose an anime that doesnt match id
f"{'💠 ' if icons else ''}Toggle auto next episode": _toggle_auto_next,
f"{'🔙 ' if icons else ''}Back": select_anime,
f"{'' if icons else ''}Exit": exit_app,
}
@@ -691,10 +730,10 @@ def select_anime(config: Config, anilist_config: QueryDict):
)
elif config.use_rofi:
# TODO: Make this faster
if config.preview and False:
from .utils import SEARCH_RESULTS_CACHE, get_preview
if config.preview:
from .utils import IMAGES_DIR, get_icons
get_preview(search_results, config, wait=True)
get_icons(search_results, config)
choices = []
for anime in search_results:
title = sanitize_filename(
@@ -703,8 +742,7 @@ def select_anime(config: Config, anilist_config: QueryDict):
or anime["title"]["romaji"]
)
)
anime_cache = os.path.join(SEARCH_RESULTS_CACHE, title)
icon_path = f"{anime_cache}/image"
icon_path = os.path.join(IMAGES_DIR, title)
choices.append(f"{title}\0icon\x1f{icon_path}")
choices.append("Back")
selected_anime_title = Rofi.run_with_icons(choices, "Select Anime")
@@ -729,8 +767,12 @@ def select_anime(config: Config, anilist_config: QueryDict):
def handle_animelist(anilist_config, config: Config, list_type: str):
if not config.user:
print("You haven't logged in please run: fastanime anilist login")
input("Enter to continue...")
if not config.use_rofi:
print("You haven't logged in please run: fastanime anilist login")
input("Enter to continue...")
else:
if not Rofi.confirm("You haven't logged in!!Enter to continue"):
exit(1)
anilist(config, anilist_config)
return
match list_type:
@@ -751,12 +793,21 @@ def handle_animelist(anilist_config, config: Config, list_type: str):
anime_list = AniList.get_anime_list(status)
if not anime_list:
print("Sth went wrong", anime_list)
input("Enter to continue")
if not config.use_rofi:
input("Enter to continue")
else:
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
exit(1)
anilist(config, anilist_config)
return
if not anime_list[0]:
print("Sth went wrong", anime_list)
input("Enter to continue")
if not config.use_rofi:
input("Enter to continue")
else:
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
exit(1)
anilist(config, anilist_config)
return
media = [
@@ -769,7 +820,10 @@ def handle_animelist(anilist_config, config: Config, list_type: str):
def anilist(config: Config, anilist_config: QueryDict):
def _anilist_search():
search_term = Prompt.ask("[cyan]Search for[/]")
if config.use_rofi:
search_term = str(Rofi.ask("Search for"))
else:
search_term = Prompt.ask("[cyan]Search for[/]")
return AniList.search(query=search_term)
@@ -834,14 +888,12 @@ def anilist(config: Config, anilist_config: QueryDict):
f"{'' if icons else ''}Exit": exit_app,
}
if config.use_fzf:
action = fzf.run(
list(options.keys()),
prompt="Select Action: ",
header="Anilist Menu",
)
elif config.use_rofi:
action = Rofi.run(list(options.keys()), "Select Action")
else:
action = fuzzy_inquirer("Select Action", options.keys())
@@ -852,5 +904,9 @@ def anilist(config: Config, anilist_config: QueryDict):
else:
print(anilist_data[1])
input("Enter to continue...")
if not config.use_rofi:
input("Enter to continue...")
else:
if not Rofi.confirm("Sth went wrong!!Enter to continue..."):
exit(1)
anilist(config, anilist_config)

View File

@@ -14,13 +14,36 @@ class QueryDict(dict):
def exit_app(*args):
import os
import shutil
import sys
from rich import print
from ...constants import APP_NAME, ICON_PATH, USER_NAME
from ...constants import USER_NAME
def is_running_in_terminal():
try:
shutil.get_terminal_size()
return (
sys.stdin.isatty()
and sys.stdout.isatty()
and os.getenv("TERM") is not None
)
except OSError:
return False
print("Have a good day :smile:", USER_NAME)
if not is_running_in_terminal():
from plyer import notification
notification.notify(
app_name=APP_NAME,
app_icon=ICON_PATH,
message=f"Have a good day {USER_NAME}",
title="Shutting down",
) # pyright:ignore
else:
from rich import print
print("Have a good day :smile:", USER_NAME)
sys.exit(0)

View File

@@ -2,11 +2,19 @@ import subprocess
from shutil import which
from sys import exit
from plyer import notification
from fastanime import APP_NAME
from ...constants import ICON_PATH
class RofiApi:
ROFI_EXECUTABLE = which("rofi")
rofi_theme = ""
rofi_theme_confirm = ""
rofi_theme_input = ""
def run_with_icons(self, options: list[str], prompt_text: str) -> str:
rofi_input = "\n".join(options)
@@ -27,6 +35,12 @@ class RofiApi:
choice = result.stdout.strip()
if not choice:
notification.notify(
app_name=APP_NAME,
app_icon=ICON_PATH,
message="FastAnime is shutting down",
title="No Valid Input Provided",
) # pyright:ignore
exit(1)
return choice
@@ -50,9 +64,75 @@ class RofiApi:
choice = result.stdout.strip()
if not choice or choice not in options:
notification.notify(
app_name=APP_NAME,
app_icon=ICON_PATH,
message="FastAnime is shutting down",
title="No Valid Input Provided",
) # pyright:ignore
exit(1)
return choice
def confirm(self, prompt_text: str) -> bool:
rofi_choices = "Yes\nNo"
if not self.ROFI_EXECUTABLE:
raise Exception("Rofi not found")
args = [self.ROFI_EXECUTABLE]
if self.rofi_theme_confirm:
args.extend(["-no-config", "-theme", self.rofi_theme_confirm])
args.extend(["-p", prompt_text, "-i", "", "-no-fixed-num-lines", "-dmenu"])
result = subprocess.run(
args,
input=rofi_choices,
stdout=subprocess.PIPE,
text=True,
)
choice = result.stdout.strip()
if not choice:
notification.notify(
app_name=APP_NAME,
app_icon=ICON_PATH,
message="FastAnime is shutting down",
title="No Valid Input Provided",
) # pyright:ignore
exit(1)
if choice == "Yes":
return True
else:
return False
def ask(
self, prompt_text: str, is_int: bool = False, is_float: bool = False
) -> str | float | int:
if not self.ROFI_EXECUTABLE:
raise Exception("Rofi not found")
args = [self.ROFI_EXECUTABLE]
if self.rofi_theme_input:
args.extend(["-no-config", "-theme", self.rofi_theme_input])
args.extend(["-p", prompt_text, "-i", "-no-fixed-num-lines", "-dmenu"])
result = subprocess.run(
args,
stdout=subprocess.PIPE,
text=True,
)
user_input = result.stdout.strip()
if not user_input:
notification.notify(
app_name=APP_NAME,
app_icon=ICON_PATH,
message="FastAnime is shutting down",
title="No Valid Input Provided",
) # pyright:ignore
exit(1)
if is_float:
user_input = float(user_input)
elif is_int:
user_input = int(user_input)
return user_input
Rofi = RofiApi()