diff --git a/fastanime/api/__init__.py b/fastanime/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fastanime/libs/fzf/__init__.py b/fastanime/libs/fzf/__init__.py new file mode 100644 index 0000000..02b038f --- /dev/null +++ b/fastanime/libs/fzf/__init__.py @@ -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 {}") diff --git a/fastanime/libs/fzf/config.py b/fastanime/libs/fzf/config.py new file mode 100644 index 0000000..6e62af0 --- /dev/null +++ b/fastanime/libs/fzf/config.py @@ -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", +] diff --git a/fastanime/libs/fzf/previews.py b/fastanime/libs/fzf/previews.py new file mode 100644 index 0000000..487e6e5 --- /dev/null +++ b/fastanime/libs/fzf/previews.py @@ -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;'