Port the fish_greeting shell greeting to the new API

The original script (J621-Django/extras/fish_greeting system) downloaded
image bytes from /random/?rating=X and read the X-File-* headers. The new
API answers with JSON and a signed link, so the greeting now:

- asks /api/random/?fastfetch=1&rating=<mode> for JSON;
- parses url/j_id/filename/md5 with jq, python3, or a grep/sed fallback;
- downloads the signed link and keeps the original display path (fastfetch
  kitty/kitty-icat logos, gifsicle preprocessing for GIFs, recursion guard,
  logging with rotation, greeting_mode 0/1/2 = NSFW/SFW/Questionable);
- is configured through ~/.config/j621Greeting/config.fish or universal
  variables (J621_BASE, J621_WEB, J621_TOKEN, J621_FASTFETCH_CONFIG) instead
  of a hardcoded host, and prints the /detail/<J-ID> link on J621_WEB;
- reports the API's own 404 message when a rating has no images, and keeps
  curl quiet so failures do not spill into the greeting.

extras/fish_greeting/ contains the function, an install.fish (copies it into
the fish functions dir, creates the config once, checks dependencies) and a
README with the old-vs-new table and troubleshooting. The existing
gm-switch helper and .desktop launchers keep working (same mode file).

Tested with fish 4.9.3: syntax check on every file, guest and token runs
against the dev API (unsigned vs signed URLs), the empty-rating and
unreachable-API paths, and the grep/sed fallback with jq and python3
unavailable.
This commit is contained in:
2026-09-18 13:14:41 -05:00
parent f8667c1037
commit aee29de34a
5 changed files with 366 additions and 2 deletions
+219
View File
@@ -0,0 +1,219 @@
# fish_greeting — show a random library image as the shell greeting.
#
# Updated for the J621 Docker/REST rewrite: the API answers with JSON that
# carries a signed URL instead of streaming the image, so this function asks
# for one random png/jpg/gif (fastfetch mode), downloads the signed link and
# hands the file to fastfetch. Based on the original script from
# J621-Django/extras/fish_greeting system/fish_greeting.fish.
#
# Requires: curl (or wget) and fastfetch; gifsicle is used for animated GIFs
# (without it, the GIF is shown as-is).
#
# Rating modes (same file as the original script):
# ~/.config/fish/greeting_mode 0 = NSFW (explicit), 1 = SFW (safe),
# 2 = Questionable (default)
#
# Configuration, any of these (all optional):
# ~/.config/j621Greeting/config.fish with `set -g VAR value` lines, or
# universal variables, e.g. `set -Ux J621_BASE https://j621.example.ts.net`
# J621_BASE API origin (default https://j621.rainbow-herring.ts.net)
# J621_TOKEN API token, sent as `Authorization: Token …`; needed for
# signed URLs and for hidden-from-guests items (optional)
# J621_WEB Web origin used in the printed link (defaults to J621_BASE)
# J621_FASTFETCH_CONFIG fastfetch config name (default "jake")
function __j621_json_field --argument-names json field
if command -v jq >/dev/null 2>&1
printf '%s' "$json" | jq -r --arg field "$field" '.[$field] // empty'
else if command -v python3 >/dev/null 2>&1
printf '%s' "$json" | python3 -c "import json,sys; value = json.load(sys.stdin).get(sys.argv[1], ''); print(value if value is not None else '')" $field
else
printf '%s' "$json" | grep -o "\"$field\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | head -1 | sed -E 's/.*:[[:space:]]*"//; s/"$//'
end
end
function __j621_fetch_random --argument-names rating
set base (string trim --right --chars=/ "$J621_BASE")
set api_url "$base/api/random/?fastfetch=1&rating=$rating"
set curl_args -s -L --max-time 20
if set -q J621_TOKEN; and test -n "$J621_TOKEN"
set -a curl_args -H "Authorization: Token $J621_TOKEN"
end
if command -v curl >/dev/null 2>&1
curl $curl_args "$api_url" | string collect
else if command -v wget >/dev/null 2>&1
wget -qO- "$api_url" | string collect
end
end
function fish_greeting --argument-names depth
# Initialize depth to 0 if not provided or empty
if not set -q depth; or test -z "$depth"
set depth 0
end
set MAX_DEPTH 3
# --- Configuration ---
set config_file ~/.config/j621Greeting/config.fish
if test -f $config_file
source $config_file
end
set -q J621_BASE; or set -g J621_BASE https://j621.rainbow-herring.ts.net
set -q J621_WEB; or set -g J621_WEB $J621_BASE
set -q J621_FASTFETCH_CONFIG; or set -g J621_FASTFETCH_CONFIG jake
set web_base (string trim --right --chars=/ "$J621_WEB")
# --- Setup logging ---
set log_dir ~/.config/j621Logos
if not test -d $log_dir
mkdir -p $log_dir
end
set log_file $log_dir/logs.log
# --- Mode selection ---
if test -f ~/.config/fish/greeting_mode
set mode (cat ~/.config/fish/greeting_mode | string trim)
else
set mode 2
end
switch $mode
case "0"
set rating e
set modename "NSFW"
case "1"
set rating s
set modename "SFW"
case "2"
set rating q
set modename "Questionable"
case "*"
echo "Unknown mode, using default NSFW"
set rating e
set modename "NSFW"
end
# --- Ask the API for a random image (JSON with a signed URL) ---
set json (__j621_fetch_random $rating)
set image_url (__j621_json_field "$json" url)
set j_id (__j621_json_field "$json" j_id)
set md5 (__j621_json_field "$json" md5)
set filename (__j621_json_field "$json" filename)
if test -z "$image_url"
set detail (__j621_json_field "$json" detail)
fastfetch --config "$J621_FASTFETCH_CONFIG"
if test -n "$detail"
printf '\e[4;2;38;2;138;0;222;49mNo logo (%s)\e[0m\n' "$detail"
else
printf '\e[4;2;38;2;138;0;222;49mNo logo (API error)\e[0m\n'
end
echo "$(date '+%Y-%m-%d %H:%M:%S') No logo for rating=$rating: $detail" >> "$log_file"
return
end
# --- Download the signed image ---
set tempfile (mktemp /tmp/fastfetch_logo_XXXXXX)
set download_success 0
if command -v curl >/dev/null 2>&1
curl -s -L --max-time 60 -o "$tempfile" "$image_url"
if test $status -eq 0 -a -s "$tempfile"
set download_success 1
end
else if command -v wget >/dev/null 2>&1
wget -q -O "$tempfile" "$image_url"
if test $status -eq 0 -a -s "$tempfile"
set download_success 1
end
end
if test $download_success -ne 1
fastfetch --config "$J621_FASTFETCH_CONFIG"
printf '\e[4;2;38;2;138;0;222;49mNo logo (download failed)\e[0m\n'
echo "$(date '+%Y-%m-%d %H:%M:%S') Download failed for $j_id" >> "$log_file"
rm -f "$tempfile"
return
end
# --- Determine MIME type ---
set mime (file --mime-type -b "$tempfile" 2>/dev/null | string trim | string collect)
# --- Accept only PNG, JPEG, GIF; everything else causes one more roll ---
if not string match -q "image/png" "$mime"; and not string match -q "image/jpeg" "$mime"; and not string match -q "image/gif" "$mime"
if test $depth -lt $MAX_DEPTH
fish_greeting (math $depth + 1)
else
printf '\e[31mMax recursion depth reached, skipping unsupported file type: %s\e[0m\n' "$mime"
end
if test $depth -eq 0
printf '\e[31mForked (Got unsupported type: %s - %s)\e[0m\n' "$mime" "$filename"
if test -n "$md5"
echo "Link: $web_base/view/$md5"
end
end
rm -f "$tempfile" "$tempfile.tmp" 2>/dev/null
return
end
# --- Process the file (only PNG, JPEG, GIF reach here) ---
set logo_type "kitty"
set processing_success 1
switch "$mime"
case "image/png" "image/jpeg"
set logo_type "kitty"
case "image/gif"
set processed_ok 0
set simple_file (mktemp /tmp/fastfetch_simple_XXXXXX)
set gif_err (mktemp)
if command -v gifsicle >/dev/null 2>&1
gifsicle --colors 255 "$tempfile" > "$simple_file" 2> "$gif_err"
set gif_exit $status
if test $gif_exit -eq 0 -a -s "$simple_file"
gifsicle --unoptimize --resize-fit 360x360 "$simple_file" > "$tempfile.tmp" 2> "$gif_err"
set final_exit $status
set gif_warnings (cat "$gif_err" | string trim)
if test $final_exit -eq 0 -a -s "$tempfile.tmp" -a -z "$gif_warnings"
mv "$tempfile.tmp" "$tempfile"
set processed_ok 1
set logo_type "kitty-icat"
echo "$(date '+%Y-%m-%d %H:%M:%S') GIF processed with gifsicle" >> "$log_file"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') gifsicle final failed: $gif_warnings" >> "$log_file"
end
else
echo "$(date '+%Y-%m-%d %H:%M:%S') gifsicle --colors failed" >> "$log_file"
end
end
rm -f "$simple_file" "$gif_err"
if test $processed_ok -eq 0
set logo_type "kitty-icat"
echo "$(date '+%Y-%m-%d %H:%M:%S') Using original GIF (processing failed)" >> "$log_file"
end
end
# --- Display result ---
if test $processing_success -eq 1
fastfetch --config "$J621_FASTFETCH_CONFIG" --logo-type "$logo_type" --logo "$tempfile" --logo-width 35
set timestamp (date '+%Y-%m-%d %H:%M:%S')
echo "$timestamp Downloaded: $j_id $filename [$modename]" >> "$log_file"
if test -n "$filename"
printf '\e[4;2;38;2;138;0;222;49mLogo from API: %s (%s, %s)\e[0m\n' "$filename" "$modename" "$j_id"
echo "Link: $web_base/detail/$j_id"
else
printf '\e[4;2;38;2;138;0;222;49mLogo from API (%s)\e[0m\n' "$modename"
end
else
fastfetch --config "$J621_FASTFETCH_CONFIG"
printf '\e[4;2;38;2;138;0;222;49mNo logo (Image processing error)\e[0m\n'
end
# --- Keep only the last 1000 lines of the log ---
set log_tmp (mktemp)
tail -n 1000 "$log_file" > "$log_tmp" 2>/dev/null
mv "$log_tmp" "$log_file"
# --- Cleanup ---
rm -f "$tempfile" "$tempfile.tmp" 2>/dev/null
end