mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-05 20:40:09 -08:00
215 lines
6.4 KiB
Python
215 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# FZF Preview Script Template
|
|
#
|
|
# This script is a template. The placeholders in curly braces, like {NAME}
|
|
# are dynamically filled by python using .replace()
|
|
|
|
from pathlib import Path
|
|
from hashlib import sha256
|
|
import subprocess
|
|
import os
|
|
import shutil
|
|
import sys
|
|
from rich.console import Console
|
|
from rich.rule import Rule
|
|
|
|
# dynamically filled variables
|
|
PREVIEW_MODE = "{PREVIEW_MODE}"
|
|
IMAGE_CACHE_DIR = Path("{IMAGE_CACHE_DIR}")
|
|
INFO_CACHE_DIR = Path("{INFO_CACHE_DIR}")
|
|
IMAGE_RENDERER = "{IMAGE_RENDERER}"
|
|
HEADER_COLOR = "{HEADER_COLOR}"
|
|
SEPARATOR_COLOR = "{SEPARATOR_COLOR}"
|
|
PREFIX = "{PREFIX}"
|
|
SCALE_UP = "{SCALE_UP}" == "True"
|
|
|
|
# fzf passes the title with quotes, so we need to trim them
|
|
TITLE = sys.argv[1]
|
|
KEY = """{KEY}"""
|
|
KEY = KEY + "-" if KEY else KEY
|
|
|
|
hash = f"{PREFIX}-{sha256((KEY + TITLE).encode('utf-8')).hexdigest()}"
|
|
|
|
|
|
def fzf_image_preview(file_path: str):
|
|
# Environment variables from fzf
|
|
FZF_PREVIEW_COLUMNS = os.environ.get("FZF_PREVIEW_COLUMNS")
|
|
FZF_PREVIEW_LINES = os.environ.get("FZF_PREVIEW_LINES")
|
|
FZF_PREVIEW_TOP = os.environ.get("FZF_PREVIEW_TOP")
|
|
KITTY_WINDOW_ID = os.environ.get("KITTY_WINDOW_ID")
|
|
GHOSTTY_BIN_DIR = os.environ.get("GHOSTTY_BIN_DIR")
|
|
PLATFORM = os.environ.get("PLATFORM")
|
|
|
|
# Compute terminal dimensions
|
|
dim = (
|
|
f"{FZF_PREVIEW_COLUMNS}x{FZF_PREVIEW_LINES}"
|
|
if FZF_PREVIEW_COLUMNS and FZF_PREVIEW_LINES
|
|
else "x"
|
|
)
|
|
|
|
if dim == "x":
|
|
try:
|
|
rows, cols = (
|
|
subprocess.check_output(
|
|
["stty", "size"], text=True, stderr=subprocess.DEVNULL
|
|
)
|
|
.strip()
|
|
.split()
|
|
)
|
|
dim = f"{cols}x{rows}"
|
|
except Exception:
|
|
dim = "80x24"
|
|
|
|
# Adjust dimension if icat not used and preview area fills bottom of screen
|
|
if (
|
|
IMAGE_RENDERER != "icat"
|
|
and not KITTY_WINDOW_ID
|
|
and FZF_PREVIEW_TOP
|
|
and FZF_PREVIEW_LINES
|
|
):
|
|
try:
|
|
term_rows = int(
|
|
subprocess.check_output(["stty", "size"], text=True).split()[0]
|
|
)
|
|
if int(FZF_PREVIEW_TOP) + int(FZF_PREVIEW_LINES) == term_rows:
|
|
dim = f"{FZF_PREVIEW_COLUMNS}x{int(FZF_PREVIEW_LINES) - 1}"
|
|
except Exception:
|
|
pass
|
|
|
|
# Helper to run commands
|
|
def run(cmd):
|
|
subprocess.run(cmd, stdout=sys.stdout, stderr=sys.stderr)
|
|
|
|
def command_exists(cmd):
|
|
return shutil.which(cmd) is not None
|
|
|
|
# ICAT / KITTY path
|
|
if IMAGE_RENDERER == "icat" and not GHOSTTY_BIN_DIR:
|
|
icat_cmd = None
|
|
if command_exists("kitten"):
|
|
icat_cmd = ["kitten", "icat"]
|
|
elif command_exists("icat"):
|
|
icat_cmd = ["icat"]
|
|
elif command_exists("kitty"):
|
|
icat_cmd = ["kitty", "icat"]
|
|
|
|
if icat_cmd:
|
|
run(
|
|
icat_cmd
|
|
+ [
|
|
"--clear",
|
|
"--transfer-mode=memory",
|
|
"--unicode-placeholder",
|
|
"--stdin=no",
|
|
f"--place={dim}@0x0",
|
|
file_path,
|
|
]
|
|
)
|
|
else:
|
|
print("No icat-compatible viewer found (kitten/icat/kitty)")
|
|
|
|
elif GHOSTTY_BIN_DIR:
|
|
try:
|
|
cols = int(FZF_PREVIEW_COLUMNS or "80") - 1
|
|
lines = FZF_PREVIEW_LINES or "24"
|
|
dim = f"{cols}x{lines}"
|
|
except Exception:
|
|
pass
|
|
|
|
if command_exists("kitten"):
|
|
run(
|
|
[
|
|
"kitten",
|
|
"icat",
|
|
"--clear",
|
|
"--transfer-mode=memory",
|
|
"--unicode-placeholder",
|
|
"--stdin=no",
|
|
f"--place={dim}@0x0",
|
|
file_path,
|
|
]
|
|
)
|
|
elif command_exists("icat"):
|
|
run(
|
|
[
|
|
"icat",
|
|
"--clear",
|
|
"--transfer-mode=memory",
|
|
"--unicode-placeholder",
|
|
"--stdin=no",
|
|
f"--place={dim}@0x0",
|
|
file_path,
|
|
]
|
|
)
|
|
elif command_exists("chafa"):
|
|
run(["chafa", "-s", dim, file_path])
|
|
|
|
elif command_exists("chafa"):
|
|
# Platform specific rendering
|
|
if PLATFORM == "android":
|
|
run(["chafa", "-s", dim, file_path])
|
|
elif PLATFORM == "windows":
|
|
run(["chafa", "-f", "sixel", "-s", dim, file_path])
|
|
else:
|
|
run(["chafa", "-s", dim, file_path])
|
|
print()
|
|
|
|
elif command_exists("imgcat"):
|
|
width, height = dim.split("x")
|
|
run(["imgcat", "-W", width, "-H", height, file_path])
|
|
|
|
else:
|
|
print(
|
|
"⚠️ Please install a terminal image viewer (icat, kitten, imgcat, or chafa)."
|
|
)
|
|
|
|
|
|
def fzf_text_preview(file_path: str):
|
|
from base64 import standard_b64encode
|
|
|
|
def serialize_gr_command(**cmd):
|
|
payload = cmd.pop("payload", None)
|
|
cmd = ",".join(f"{k}={v}" for k, v in cmd.items())
|
|
ans = []
|
|
w = ans.append
|
|
w(b"\033_G")
|
|
w(cmd.encode("ascii"))
|
|
if payload:
|
|
w(b";")
|
|
w(payload)
|
|
w(b"\033\\")
|
|
return b"".join(ans)
|
|
|
|
def write_chunked(**cmd):
|
|
data = standard_b64encode(cmd.pop("data"))
|
|
while data:
|
|
chunk, data = data[:4096], data[4096:]
|
|
m = 1 if data else 0
|
|
sys.stdout.buffer.write(serialize_gr_command(payload=chunk, m=m, **cmd))
|
|
sys.stdout.flush()
|
|
cmd.clear()
|
|
|
|
with open(file_path, "rb") as f:
|
|
write_chunked(a="T", f=100, data=f.read())
|
|
|
|
|
|
console = Console(force_terminal=True, color_system="truecolor")
|
|
if PREVIEW_MODE == "image" or PREVIEW_MODE == "full":
|
|
preview_image_path = IMAGE_CACHE_DIR / f"{hash}.png"
|
|
if preview_image_path.exists():
|
|
fzf_image_preview(str(preview_image_path))
|
|
print()
|
|
else:
|
|
print("🖼️ Loading image...")
|
|
|
|
console.print(Rule(style=f"rgb({SEPARATOR_COLOR})"))
|
|
if PREVIEW_MODE == "text" or PREVIEW_MODE == "full":
|
|
preview_info_path = INFO_CACHE_DIR / f"{hash}.py"
|
|
if preview_info_path.exists():
|
|
subprocess.run(
|
|
[sys.executable, str(preview_info_path), HEADER_COLOR, SEPARATOR_COLOR]
|
|
)
|
|
else:
|
|
console.print("📝 Loading details...")
|