Files
J621/README.md
T
JakeBreath aee29de34a 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.
2026-09-18 13:14:41 -05:00

2.6 KiB

J621

Self-hosted media library and e621 archive manager, rebuilt as a React SPA + Django REST API.

Structure

backend/    Django 6 + DRF API (MariaDB + Redis via docker compose)
frontend/   Vite + React + TypeScript SPA

Development

Backend

cd backend
source venv/bin/activate
python manage.py migrate
python manage.py scan_files            # index the watched folder
python manage.py runserver

Copy .env.example to .env and set WATCHED_FOLDER before scanning.

Frontend

cd frontend
npm install
npm run dev                            # http://localhost:5173, proxies /api to :8000

Production

Docker: see deploy/ for the two images (SPA on static nginx, API on gunicorn), the three compose variants (both / frontend-only / backend-only) behind a shared nginx service and a Tailscale sidecar, and the public-funnel or tailnet-only serve configs. deploy/push_*.sh builds and pushes the multi-arch images to the Gitea registry.

Random image endpoint

Used by the SPA's Random page and by shell greetings (fish_greeting + fastfetch):

curl -H "Authorization: Token <token>" \
  "https://j621.example.ts.net/api/random/?rating=s,q&fastfetch=1"
{
  "j_id": "J-59",
  "filename": "J-59.jpg",
  "extension": "jpg",
  "rating": "e",
  "url": "https://j621.example.ts.net/api/files/J-59/raw/?sig=…",
  "download_url": "https://j621.example.ts.net/api/files/J-59/raw/?sig=…&download=1",
  "thumbnail_url": "https://j621.example.ts.net/api/files/J-59/thumbnail/?sig=…",
  "fastfetch": true
}
  • rating — comma separated subset of s, q, e (default: any).
  • fastfetch=1, or any request whose User-Agent contains fastfetch, limits the roll to png/jpg/gif so terminals can display it. Images are the only candidates in both modes.
  • url is absolute, and signed for authenticated callers, so fastfetch can load it without headers. Guests get an unsigned URL and only see guest-visible items.
  • /random and /random/ are aliases of /api/random/ for scripts. Behind the bundled nginx those aliases negotiate on Accept: browsers get the SPA page, requesters like curl/wget/fastfetch get the JSON. /api/random/ is the unambiguous path for scripts; 404 when nothing matches the filters.

Licence

Source-available, non-commercial: personal and other non-commercial use is welcome under the Jake Labs Non-Commercial Software Licence, which requires attribution and keeps derivative works under the same licence. Commercial use is not permitted. Third-party dependencies keep their own licences (all permissive: MIT, BSD, Apache-2.0, ISC).