mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-01-06 17:53:40 -08:00
feat:switch to pure fzf for menus
This commit is contained in:
0
fastanime/api/__init__.py
Normal file
0
fastanime/api/__init__.py
Normal file
137
fastanime/libs/fzf/__init__.py
Normal file
137
fastanime/libs/fzf/__init__.py
Normal file
@@ -0,0 +1,137 @@
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Callable, List
|
||||
|
||||
from art import text2art
|
||||
from rich import print
|
||||
|
||||
from ... import PLATFORM
|
||||
from .config import FZF_DEFAULT_OPTS, FzfOptions
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# fzf\
|
||||
# --info=hidden \
|
||||
# --layout=reverse \
|
||||
# --height=100% \
|
||||
# --prompt="Select Channel: " \
|
||||
# --header="$fzf_header" \
|
||||
# --preview-window=left,50%\
|
||||
# --bind=right:accept \
|
||||
# --expect=shift-left,shift-right\
|
||||
# --tabstop=1 \
|
||||
# --no-margin \
|
||||
# +m \
|
||||
# -i \
|
||||
# --exact \
|
||||
|
||||
|
||||
def clear():
|
||||
if PLATFORM == "Windows":
|
||||
os.system("cls")
|
||||
else:
|
||||
os.system("clear")
|
||||
|
||||
|
||||
class FZF:
|
||||
if not os.getenv("FZF_DEFAULT_OPTS"):
|
||||
os.environ["FZF_DEFAULT_OPTS"] = FZF_DEFAULT_OPTS
|
||||
FZF_EXECUTABLE = shutil.which("fzf")
|
||||
default_options = [
|
||||
"--cycle",
|
||||
"--info=hidden",
|
||||
"--layout=reverse",
|
||||
"--height=100%",
|
||||
"--bind=right:accept",
|
||||
"--no-margin",
|
||||
"+m",
|
||||
"-i",
|
||||
"--expect=shift-left,shift-right",
|
||||
"--exact",
|
||||
"--tabstop=1",
|
||||
"--preview-window=left,35%,wrap",
|
||||
"--wrap",
|
||||
]
|
||||
|
||||
def _with_filter(self, command: str, work: Callable) -> List[str]:
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
shell=True,
|
||||
)
|
||||
except subprocess.SubprocessError as e:
|
||||
print(f"Failed to start subprocess: {e}", file=sys.stderr)
|
||||
return []
|
||||
|
||||
original_stdout = sys.stdout
|
||||
sys.stdout = process.stdin
|
||||
|
||||
try:
|
||||
work()
|
||||
if process.stdin:
|
||||
process.stdin.close()
|
||||
except Exception as e:
|
||||
print(f"Exception during work execution: {e}", file=sys.stderr)
|
||||
finally:
|
||||
sys.stdout = original_stdout
|
||||
|
||||
output = []
|
||||
if process.stdout:
|
||||
output = process.stdout.read().splitlines()
|
||||
process.stdout.close()
|
||||
|
||||
return output
|
||||
|
||||
def _run_fzf(self, commands: list[FzfOptions], _fzf_input) -> str:
|
||||
fzf_input = "\n".join(_fzf_input)
|
||||
|
||||
if not self.FZF_EXECUTABLE:
|
||||
raise Exception("fzf executable not found")
|
||||
|
||||
result = subprocess.run(
|
||||
[self.FZF_EXECUTABLE, *commands],
|
||||
input=fzf_input,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
if not result or result.returncode != 0 or not result.stdout:
|
||||
print("sth went wrong:confused:")
|
||||
input("press enter to try again...")
|
||||
clear()
|
||||
return self._run_fzf(commands, _fzf_input)
|
||||
clear()
|
||||
|
||||
return result.stdout.strip()
|
||||
|
||||
def run(
|
||||
self,
|
||||
fzf_input: list[str],
|
||||
prompt: str,
|
||||
header: str,
|
||||
preview: str | None = None,
|
||||
) -> str:
|
||||
_commands = [
|
||||
*self.default_options,
|
||||
"--header",
|
||||
text2art(header),
|
||||
"--header-first",
|
||||
"--prompt",
|
||||
prompt.title(),
|
||||
] # pyright:ignore
|
||||
|
||||
if preview:
|
||||
_commands.append(f"--preview={preview}")
|
||||
return self._run_fzf(_commands, fzf_input) # pyright:ignore
|
||||
|
||||
|
||||
fzf = FZF()
|
||||
|
||||
if __name__ == "__main__":
|
||||
fzf.run([*os.listdir(), "exit"], "Prompt: ", "Header", preview="bat {}")
|
||||
90
fastanime/libs/fzf/config.py
Normal file
90
fastanime/libs/fzf/config.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from typing import Literal
|
||||
|
||||
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
|
||||
--color=border:#262626,label:#aeaeae,query:#d9d9d9
|
||||
--border="rounded" --border-label="" --preview-window="border-rounded" --prompt="> "
|
||||
--marker=">" --pointer="◆" --separator="─" --scrollbar="│"
|
||||
"""
|
||||
|
||||
fzf_options = {
|
||||
"--height": "Specifies the height of fzf's interface as a percentage.",
|
||||
"--layout": 'Specifies the layout of fzf (default: "default").',
|
||||
"--reverse": "Search result list appears in reverse order.",
|
||||
"--border": "Draws border around the finder.",
|
||||
"--inline-info": "Displays finder info inline instead of at the bottom.",
|
||||
"--header": "Prints a header at the top of the finder.",
|
||||
"--header-lines": "Keeps the first N lines of the input at the top.",
|
||||
"--prompt": 'Changes the prompt string (default: "> ").',
|
||||
"--multi": "Enables multi-select with tab/shift-tab.",
|
||||
"--preview": "Displays the output of the command as preview.",
|
||||
"--preview-window": "Specifies the layout of the preview window.",
|
||||
"--bind": "Binds key to action.",
|
||||
"--color": "Specifies the color scheme.",
|
||||
"--no-sort": "Disables the sorting of the result list.",
|
||||
"--nth": 'Specifies the fields to be matched (default: "1").',
|
||||
"--delimiter": "Specifies the delimiter to use when tokenizing.",
|
||||
"--tiebreak": "Specifies the criteria to break ties.",
|
||||
"--toggle-sort": "Toggles the sorting feature.",
|
||||
"--with-nth": "Specifies the fields to be transformed.",
|
||||
"--expect": "Specifies keys to expect for instant results.",
|
||||
"--no-mouse": "Disables mouse interaction.",
|
||||
"--margin": "Specifies the margin around the finder.",
|
||||
"--no-hscroll": "Disables horizontal scrolling.",
|
||||
"--no-bold": "Disables bold text.",
|
||||
"--tabstop": "Specifies the number of spaces for a tab.",
|
||||
"--cycle": "Cycles through the result list.",
|
||||
"--phony": "Disables searching and forces all items to be shown.",
|
||||
"--filepath-word": "Matches by the filename only.",
|
||||
"--info": "Specifies where to display finder info.",
|
||||
"--jump-labels": "Specifies the labels for jump and select mode.",
|
||||
"--history": "Specifies the history file for the finder.",
|
||||
"--history-size": "Specifies the size of the history.",
|
||||
"--disabled": "Starts the finder in disabled mode.",
|
||||
"--no-unicode": "Disables Unicode characters in fzf.",
|
||||
"--separator": "Specifies the separator for multi-select.",
|
||||
"--select-1": "Automatically selects if only one match.",
|
||||
"--exit-0": "Exits with status 0 if there's no match.",
|
||||
}
|
||||
|
||||
FzfOptions = Literal[
|
||||
"--height",
|
||||
"--layout",
|
||||
"--reverse",
|
||||
"--border",
|
||||
"--inline-info",
|
||||
"--header",
|
||||
"--header-lines",
|
||||
"--prompt",
|
||||
"--multi",
|
||||
"--preview",
|
||||
"--preview-window",
|
||||
"--color",
|
||||
"--no-sort",
|
||||
"--nth",
|
||||
"--delimiter",
|
||||
"--tiebreak",
|
||||
"--toggle-sort",
|
||||
"--with-nth",
|
||||
"--expect",
|
||||
"--no-mouse",
|
||||
"--margin",
|
||||
"--no-hscroll",
|
||||
"--no-bold",
|
||||
"--tabstop",
|
||||
"--cycle",
|
||||
"--phony",
|
||||
"--info",
|
||||
"--jump-labels",
|
||||
"--history",
|
||||
"--history-size",
|
||||
"--bind",
|
||||
"--disabled",
|
||||
"--no-unicode",
|
||||
"--separator",
|
||||
"--select-1",
|
||||
"--exit-0",
|
||||
]
|
||||
19
fastanime/libs/fzf/previews.py
Normal file
19
fastanime/libs/fzf/previews.py
Normal file
@@ -0,0 +1,19 @@
|
||||
preview_anime_search = 'hght=$(($FZF_PREVIEW_COLUMNS/2 +2));\
|
||||
i=$(echo {}|sed "s/\\t.*$//g");\
|
||||
echo $i>$HOME/.cache/magic-tape/search/channels/index.txt;\
|
||||
TITLE="$(cat $HOME/.cache/magic-tape/search/channels/titles.txt|head -$i|tail +$i)";\
|
||||
if [[ "$IMAGE_SUPPORT" != "none" ]]&&[[ "$IMAGE_SUPPORT" != "chafa" ]];then ll=0;while [ $ll -le $(($hght/2 - 2)) ];do echo "";((ll++));done;fi;\
|
||||
ll=1; echo -ne "\x1b[38;5;241m"; while [ $ll -le $FZF_PREVIEW_COLUMNS ];do echo -n -e "─";((ll++));done;echo -n -e "$normal";\
|
||||
if [[ "$TITLE" == "Previous Page" ]];then draw_preview $(($hght/3)) 1 $(($FZF_PREVIEW_COLUMNS/2)) $(($FZF_PREVIEW_COLUMNS/2)) $HOME/.cache/magic-tape/png/previous.png;\
|
||||
elif [[ "$TITLE" == "Next Page" ]];then draw_preview $(($hght/3)) 1 $(($FZF_PREVIEW_COLUMNS/2)) $(($FZF_PREVIEW_COLUMNS/2)) $HOME/.cache/magic-tape/png/next.png;\
|
||||
elif [[ "$TITLE" == "Abort Selection" ]];then draw_preview $(($hght/3)) 1 $(($FZF_PREVIEW_COLUMNS/2)) $(($FZF_PREVIEW_COLUMNS/2)) $HOME/.cache/magic-tape/png/abort.png;\
|
||||
else draw_preview $(($hght/3)) 1 $(($FZF_PREVIEW_COLUMNS/2)) $(($FZF_PREVIEW_COLUMNS/2)) $HOME/.cache/magic-tape/jpg/"$(cat $HOME/.cache/magic-tape/search/channels/ids.txt|head -$i|tail +$i)".jpg;fi;\
|
||||
echo -e "\n""$Yellow""$TITLE""$normal"|fold -w $FZF_PREVIEW_COLUMNS -s;\
|
||||
ll=1; echo -ne "\x1b[38;5;241m"; while [ $ll -le $FZF_PREVIEW_COLUMNS ];do echo -n -e "─";((ll++));done;echo -n -e "$normal";\
|
||||
if [[ $TITLE != "Abort Selection" ]]&&[[ $TITLE != "Next Page" ]]&&[[ $TITLE != "Previous Page" ]];\
|
||||
then SUBS="$(cat $HOME/.cache/magic-tape/search/channels/subscribers.txt|head -$i|tail +$i)";\
|
||||
echo -e "\n"$Green"Subscribers: ""$Cyan""$SUBS""$normal";\
|
||||
ll=1; echo -ne "\x1b[38;5;241m"; while [ $ll -le $FZF_PREVIEW_COLUMNS ];do echo -n -e "─";((ll++));done;echo -n -e "$normal";\
|
||||
DESCRIPTION="$(cat $HOME/.cache/magic-tape/search/channels/descriptions.txt|head -$i|tail +$i)";\
|
||||
echo -e "\n\x1b[38;5;250m$DESCRIPTION"$normal""|fold -w $FZF_PREVIEW_COLUMNS -s; \
|
||||
fi;'
|
||||
Reference in New Issue
Block a user