feat: experimental search using fzf reload

This commit is contained in:
Benexl
2025-07-04 16:20:48 +03:00
parent f46f09ffdf
commit 3bdfa27e1c
3 changed files with 126 additions and 4 deletions

View File

@@ -1633,7 +1633,9 @@ def media_actions_menu(
return
relations = relations[1]["data"]["Media"]["relations"] # pyright:ignore
relations["nodes"] = [node for node in relations["nodes"] if node.get("type") == "ANIME"]
relations["nodes"] = [
node for node in relations["nodes"] if node.get("type") == "ANIME"
]
fastanime_runtime_state.anilist_results_data = {
"data": {"Page": {"media": relations["nodes"]}} # pyright:ignore
}
@@ -1940,6 +1942,8 @@ def _anilist_search(config: "Config", page=1):
# TODO: Add filters and other search features
if config.use_rofi:
search_term = str(Rofi.ask("Search for"))
elif config.use_fzf:
search_term = fzf.search_for_anime()
else:
search_term = Prompt.ask("[cyan]Search for[/]")

View File

@@ -9,6 +9,7 @@ from click import clear
from rich import print
from ...cli.utils.tools import exit_app
from .scripts import FETCH_ANIME_SCRIPT
logger = logging.getLogger(__name__)
@@ -129,7 +130,7 @@ class FZF:
encoding="utf-8",
)
if not result or result.returncode != 0 or not result.stdout:
if result.returncode == 130: # fzf terminated by ctrl-c
if result.returncode == 130: # fzf terminated by ctrl-c
exit_app()
print("sth went wrong :confused:")
@@ -198,9 +199,50 @@ class FZF:
# os.environ["FZF_DEFAULT_OPTS"] = ""
return result
def search_for_anime(self):
commands = [
"--preview",
f"{FETCH_ANIME_SCRIPT}fetch_anime_details {{}}",
"--prompt",
"Search For Anime: ",
"--header",
"Type to search, results are dynamically loaded, enter to select",
"--bind",
f"change:reload({FETCH_ANIME_SCRIPT}fetch_anime_for_fzf {{q}})",
"--preview-window",
"wrap",
# "--bind",
# f"enter:become(echo {{}})",
"--reverse",
]
if not self.FZF_EXECUTABLE:
raise Exception("fzf executable not found")
os.environ["SHELL"] = "bash"
result = subprocess.run(
[self.FZF_EXECUTABLE, *commands],
input="",
stdout=subprocess.PIPE,
universal_newlines=True,
text=True,
encoding="utf-8",
)
if not result or result.returncode != 0 or not result.stdout:
if result.returncode == 130: # fzf terminated by ctrl-c
exit_app()
print("sth went wrong :confused:")
input("press enter to try again...")
clear()
clear()
return result.stdout.strip().split("|")[0].strip()
fzf = FZF()
if __name__ == "__main__":
action = fzf.run([*os.listdir(), "exit"], "Prompt: ", "Header", preview="bat {}")
print(action)
print(fzf.search_for_anime())
exit()

View File

@@ -0,0 +1,76 @@
FETCH_ANIME_SCRIPT = r"""
fetch_anime_for_fzf() {
local search_term="$1"
if [ -z "$search_term" ]; then exit 0; fi
local query='
query ($search: String) {
Page(page: 1, perPage: 25) {
media(search: $search, type: ANIME, sort: [SEARCH_MATCH]) {
id
title { romaji english }
meanScore
format
status
}
}
}
'
local json_payload
json_payload=$(jq -n --arg query "$query" --arg search "$search_term" \
'{query: $query, variables: {search: $search}}')
curl --silent \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--request POST \
--data "$json_payload" \
https://graphql.anilist.co | \
jq -r '.data.Page.media[]? | select(.title.romaji) |
"\(.title.english // .title.romaji) | Score: \(.meanScore // "N/A") | ID: \(.id)"'
}
fetch_anime_details() {
local anime_id
anime_id=$(echo "$1" | sed -n 's/.*ID: \([0-9]*\).*/\1/p')
if [ -z "$anime_id" ]; then echo "Select an item to see details..."; return; fi
local query='
query ($id: Int) {
Media(id: $id, type: ANIME) {
title { romaji english }
description(asHtml: false)
genres
meanScore
episodes
status
format
season
seasonYear
studios(isMain: true) { nodes { name } }
}
}
'
local json_payload
json_payload=$(jq -n --arg query "$query" --argjson id "$anime_id" \
'{query: $query, variables: {id: $id}}')
# Fetch and format details for the preview window
curl --silent \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--request POST \
--data "$json_payload" \
https://graphql.anilist.co | \
jq -r '
.data.Media |
"Title: \(.title.english // .title.romaji)\n" +
"Score: \(.meanScore // "N/A") | Episodes: \(.episodes // "N/A")\n" +
"Status: \(.status // "N/A") | Format: \(.format // "N/A")\n" +
"Season: \(.season // "N/A") \(.seasonYear // "")\n" +
"Genres: \(.genres | join(", "))\n" +
"Studio: \(.studios.nodes[0].name // "N/A")\n\n" +
"\(.description | gsub("<br><br>"; "\n\n") | gsub("<[^>]*>"; "") | gsub("&quot;"; "\""))"
'
}
"""