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:
@@ -0,0 +1,74 @@
|
||||
# fish_greeting (updated for the J621 rewrite)
|
||||
|
||||
Shows a random library image as your shell greeting, using fastfetch. This is
|
||||
the updated version of the script from
|
||||
`J621-Django/extras/fish_greeting system/fish_greeting.fish`, adapted to the
|
||||
new REST API.
|
||||
|
||||
## What changed from the old script
|
||||
|
||||
| | Old J621-Django | This version |
|
||||
| --- | --- | --- |
|
||||
| Endpoint | `GET /random/?rating=X` returned **image bytes** | `GET /api/random/?fastfetch=1&rating=X` returns **JSON** |
|
||||
| Image access | same response, `X-File-MD5`/`X-File-Name` headers | signed `url` from the JSON, downloaded separately |
|
||||
| Unsupported types | rolled again (recursion) | server only returns png/jpg/gif in fastfetch mode |
|
||||
| Config | hardcoded `https://j621.jake.i` | `J621_BASE` / `J621_TOKEN` / `J621_WEB` |
|
||||
| Printed link | `/view/<md5>` on the old host | `/detail/<J-ID>` on `J621_WEB` |
|
||||
| Modes, logging, gifsicle, fastfetch flags | same | same (`~/.config/fish/greeting_mode`, `~/.config/j621Logos/logs.log`) |
|
||||
|
||||
The `gm-switch` helper and the `.desktop` launchers from the old repo keep
|
||||
working unchanged: they write the same `~/.config/fish/greeting_mode` file
|
||||
(0 = NSFW, 1 = SFW, 2 = Questionable).
|
||||
|
||||
## Install
|
||||
|
||||
```fish
|
||||
cd extras/fish_greeting
|
||||
fish install.fish
|
||||
```
|
||||
|
||||
Then edit `~/.config/j621Greeting/config.fish` (at least `J621_BASE`) and test:
|
||||
|
||||
```fish
|
||||
fish_greeting
|
||||
```
|
||||
|
||||
Requirements: `curl` (or `wget`), `fastfetch`, `file`. Optional: `gifsicle`
|
||||
(GIF downscaling), `jq` or `python3` (JSON parsing — without either it falls
|
||||
back to grep/sed).
|
||||
|
||||
## Configuration
|
||||
|
||||
Any of these work; the config file is read by the function on every run:
|
||||
|
||||
```fish
|
||||
# ~/.config/j621Greeting/config.fish, or universal variables
|
||||
set -g J621_BASE https://j621.rainbow-herring.ts.net # API origin
|
||||
set -g J621_WEB https://j621.example.ts.net # link origin (split deploys)
|
||||
set -g J621_TOKEN <api token> # signed URLs + hidden items
|
||||
set -g J621_FASTFETCH_CONFIG jake # fastfetch config name
|
||||
```
|
||||
|
||||
`J621_TOKEN` is the same token the SPA uses (`Authorization: Token …`). With
|
||||
it the API signs the image URL for your account, so you also get items that
|
||||
are hidden from guests. Without it the greeting runs as a guest and sees the
|
||||
public library only.
|
||||
|
||||
## Rating filters
|
||||
|
||||
| `greeting_mode` | Rating requested | Label |
|
||||
| --- | --- | --- |
|
||||
| `0` | `e` | NSFW |
|
||||
| `1` | `s` | SFW |
|
||||
| `2` (default) | `q` | Questionable |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- `No logo (No image matches those filters.)` — the library has no images for
|
||||
that rating; try another mode or add files.
|
||||
- `No logo (API error)` — check `J621_BASE`, and that the deployment is up
|
||||
(`https://<host>/health`).
|
||||
- `No logo (download failed)` — the signed URL expired (they last 24 h) or the
|
||||
item was deleted between the two requests; just run it again.
|
||||
- The greeting is slow — the API and image fetch have `--max-time` guards; a
|
||||
slow tailnet link is usually the cause.
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copy to ~/.config/j621Greeting/config.fish and adjust. All of these can also
|
||||
# be universal variables, e.g. `set -Ux J621_BASE https://j621.example.ts.net`.
|
||||
|
||||
# API origin (no trailing slash needed).
|
||||
set -g J621_BASE https://j621.rainbow-herring.ts.net
|
||||
|
||||
# Web origin used in the printed link. Only needed when the SPA is served
|
||||
# from a different host than the API (split deployment).
|
||||
# set -g J621_WEB https://j621-frontend.rainbow-herring.ts.net
|
||||
|
||||
# API token. Optional: gives you signed image URLs and access to items that
|
||||
# are hidden from guests. Create one with `manage.py drf_create_token <user>`
|
||||
# or copy it from the app (it is the same token the SPA stores).
|
||||
# set -g J621_TOKEN paste-your-token-here
|
||||
|
||||
# fastfetch config name used for the greeting logo.
|
||||
# set -g J621_FASTFETCH_CONFIG jake
|
||||
@@ -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
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env fish
|
||||
# Install the J621 fish greeting:
|
||||
# * copies fish_greeting.fish into ~/.config/fish/functions/
|
||||
# * creates ~/.config/j621Greeting/config.fish from the example (once)
|
||||
# * checks the tools it needs
|
||||
#
|
||||
# Usage: fish install.fish
|
||||
|
||||
set -l here (path resolve (dirname (status --current-filename)))
|
||||
set -l functions_dir ~/.config/fish/functions
|
||||
set -l config_dir ~/.config/j621Greeting
|
||||
set -l problems 0
|
||||
|
||||
mkdir -p $functions_dir
|
||||
cp "$here/fish_greeting.fish" "$functions_dir/fish_greeting.fish"
|
||||
echo "Installed $functions_dir/fish_greeting.fish"
|
||||
|
||||
mkdir -p $config_dir
|
||||
if test -f "$config_dir/config.fish"
|
||||
echo "Keeping existing $config_dir/config.fish"
|
||||
else
|
||||
cp "$here/config.example.fish" "$config_dir/config.fish"
|
||||
echo "Created $config_dir/config.fish — edit J621_BASE (and J621_TOKEN) there"
|
||||
end
|
||||
|
||||
for tool in curl fastfetch file
|
||||
if not command -v $tool >/dev/null 2>&1
|
||||
set problems 1
|
||||
echo "Missing required tool: $tool"
|
||||
end
|
||||
end
|
||||
|
||||
if not command -v curl >/dev/null 2>&1; and not command -v wget >/dev/null 2>&1
|
||||
set problems 1
|
||||
echo "Missing required tool: curl (or wget)"
|
||||
end
|
||||
|
||||
for tool in gifsicle jq python3
|
||||
if not command -v $tool >/dev/null 2>&1
|
||||
echo "Optional tool not found: $tool (the greeting still works)"
|
||||
end
|
||||
end
|
||||
|
||||
if test $problems -eq 0
|
||||
echo
|
||||
echo "Done. Test it now with: fish_greeting"
|
||||
echo "Rating mode lives in ~/.config/fish/greeting_mode (0=NSFW, 1=SFW, 2=Questionable)."
|
||||
echo "The old gm-switch tool / .desktop launchers keep working — same file."
|
||||
else
|
||||
echo
|
||||
echo "Install the missing tools, then run: fish_greeting"
|
||||
end
|
||||
Reference in New Issue
Block a user