A desktop app for browsing and watching anime.
ani-gui is a graphical front-end for pystardust/ani-cli. It embeds the upstream Bash scraper unmodified and wraps it in a Rust + SvelteKit desktop application — discovery, search, an embedded player, downloads, persistent watch history, Picture-in-Picture, and OP/ED skip on top of the same scraping engine.
The CLI still exists. The GUI does not replace it; the two share the script and coexist in this repository. See docs/architecture.md for the full picture.
| Discovery | Trending, This Season, Top Rated, Recently Released — AniList + Kitsu. |
| Search | Full-text against Kitsu, instant as you type. |
| Detail page | Synopsis, episodes with thumbnails, similar-titles strip. |
| Embedded player | HLS / MP4, quality switch, native or custom controls — no mpv window. |
| Subtitles | Upstream .vtt via <track kind="subtitles">. |
| OP / ED skip | aniskip intervals — one-click or fully automatic. |
| Picture-in-Picture | Persists across navigation. |
| Background prefetch | Adjacent episodes warm in advance. |
| Downloads | Per-episode or ranged, progress dock. aria2c bundled; ffmpeg sourced per platform (apt Recommends: on .deb, installer-time fetch on Windows, system PATH on AppImage). |
| Shared history | Continue Watching reads/writes $XDG_STATE_HOME/ani-cli/ani-hsts — same file as the CLI. Remove a single card or clear the lot from the rail. |
| External player | One click to mpv / VLC / IINA / custom. |
| Watch together | Hand the current stream to Syncplay for a watch party. |
| Trackers | Connect AniList or MyAnimeList — a Watch Later rail on the home page, and your progress synced back automatically as you watch. |
| Localised | English, Brazilian Portuguese, Latin American Spanish, Russian. |
| No telemetry | No analytics or tracking. Outbound traffic is metadata, the stream you picked, launch-time update checks (the app's own GitHub releases + the bundled script's self-update), and — with an account connected — your tracker's list + progress sync. Localhost-only listener on a kernel-assigned port. See the privacy policy. |
ani-gui is distributed as a desktop bundle. The bundled script is updated automatically on launch (see Self-update below); a separate ani-cli install is not required.
Platform support tiers:
| Tier | Platform | Status |
|---|---|---|
| 1 | Linux | Actively tested on Ubuntu. Other distros work via AppImage. |
| 2 | Windows | Most features verified end-to-end. Edge cases may surface. |
| 3 | macOS | Untested. Builds the same way; should work — please file an issue if it doesn't. |
Linux — tier 1 (tested on Ubuntu)
- AppImage — download from the releases page,
chmod +x, double-click. The bundle launches with Chromium's setuid sandbox disabled (AppImage's read-only FUSE mount can't carry the SUID bitchrome-sandboxrequires); the localhost-only architecture means the sandbox isn't load-bearing for the threat model. If you'd rather keep the sandbox, install the.debinstead. - Debian / Ubuntu (
.deb) —sudo apt install ./ani-gui_<version>_amd64.deb. apt pulls in the recommendedffmpegpackage (needed for the download feature) along the way; the post-install script sets thechrome-sandboxSUID bit Electron needs, so the sandbox stays on.sudo dpkg -i …still works but won't auto-install ffmpeg — drop intoapt --fix-broken installor runsudo apt install ffmpegseparately if you used dpkg directly.
Windows — tier 2 (most functions tested)
NSIS installer (.exe). Run it; it installs per-user by default and creates Start menu and desktop shortcuts.
ani-gui drives the upstream ani-cli Bash script via bash, which on Windows ships as part of Git for Windows. If Git for Windows isn't installed when you launch the app, you'll see a dialog with a one-click link to its download page. The installer will fetch ffmpeg automatically the first time it runs (~80 MB) so the download feature works out of the box; aria2c and fzf are bundled directly. The ffmpeg fetch runs even when you already have ffmpeg installed via a per-user package manager (scoop, winget user-scope) — the installer's elevated context doesn't see per-user PATH entries, and the bundled copy is what the app uses at runtime in either case.
macOS — untested
A .dmg is produced by the same electron-builder config and should install via the standard drag-into-Applications flow. macOS isn't part of the regular acceptance pass, so if you hit a problem please open an issue — the app is shipped for it but unverified.
Tested on Linux. The dev loop (steps 5–6) works the same on macOS and Windows; the packaging scripts (step 7) build per-host artifacts — run on Linux for .AppImage / .deb, on Windows for the NSIS installer.
- Install Rust (toolchain pinned by
rust-toolchain.toml):curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh . "$HOME/.cargo/env" # (or re-open the shell) so `cargo` is on PATH
- Install Node 20+ and pnpm (via nvm — skip the curl step if you already have nvm or installed Node another way):
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash # re-open the shell (or `source ~/.bashrc`) so nvm is on PATH nvm install 20 && nvm use 20 corepack enable && corepack prepare pnpm@latest --activate
- System build deps (Linux):
sudo apt install -y build-essential libssl-dev pkg-config
- Clone and install JS deps:
git clone https://github.com/JoaoPucci/ani-gui.git && cd ani-gui (cd gui/frontend && pnpm install) (cd gui/electron && pnpm install)
- Build the backend binary (required before the first run, and after every Rust change):
cd gui/backend && cargo build --bin ani-gui-backend
- Run the dev app — two terminals, started in this order:
# Terminal A — Vite dev server, HMR on :5173 cd gui/frontend && pnpm dev # Terminal B — Electron shell, spawns the backend binary from step 5 cd gui/electron && pnpm dev
- Build a distributable bundle:
macOS
cd gui/electron # Linux host pnpm package # .AppImage — fast iteration pnpm package:release # .AppImage + .deb # Windows host (Git Bash / PowerShell, with Rust + Node + pnpm installed natively; # `fetch:win-deps` needs `bsdtar`, which Git for Windows already ships) pnpm package:win # NSIS installer
.dmgis produced by CI on amacos-*runner — for local mac builds seedocs/development.md.
For lints, git hooks, and the bash test toolchain see docs/development.md.
On first launch the app:
- Spawns the Rust sidecar on a kernel-assigned localhost port (no fixed port, no internet-reachable service).
- Materialises the bundled
ani-cliscript to$XDG_CACHE_HOME/ani-gui/ani-cliso it can be patched in place by-U. - Runs
bash ani-cli -Uin the background to pick up any same-day upstream hotfixes. - Loads the discovery surface.
After that, click anything that looks clickable. The app routes the click through Kitsu / AniList for metadata, ani-cli for the actual stream resolution, and the embedded player for playback.
Connecting a list provider is optional — the app works fully without an account. From the Account page you can connect AniList or MyAnimeList (OAuth in your browser; the token is stored with your OS keychain via Electron's safeStorage, never in plaintext).
Once connected:
- Watch Later rail — your Plan-to-Watch list surfaces as a rail on the home page, bridged to local cards you can play in one click.
- Automatic progress sync — as you watch, the episode is pushed back to the tracker. The sync only ever moves progress forward (replaying or stepping back never lowers your count), promotes a Plan-to-Watch title to Watching on first play, preserves a Rewatching row, and marks a series Completed when you start the last episode of a finished show.
Everything stays on your machine: your OAuth token is encrypted through your OS keychain and written to the app's user-data directory (the Rust backend never persists it — each request carries its own bearer), and your tracker list is cached in a local SQLite database to render the Watch Later rail. ani-gui runs no server of its own. See the privacy policy for exactly what's sent where.
User settings live in $XDG_CONFIG_HOME/ani-gui/config.toml. The Settings page exposes everything you'd normally edit:
- audio mode (
sub/dub) and quality (best,1080,720,480,worst) - UI locale
- external-player kind and command
- image-cache size cap
- auto-play next episode
- auto-skip OP / ED
- custom-vs-native player controls
- whether to enter PiP automatically when you navigate away from a playing video
- whether to keep
ani-cliself-updating on launch
Full table with defaults and effects is in docs/architecture.md.
Allmanga (the catalogue ani-cli scrapes) changes its API often, and upstream pystardust/ani-cli ships hotfixes daily. The bundled snapshot in your install would go stale within a week.
ani-gui handles this for you: on every launch a background task runs bash <cached-ani-cli> -U, captures the outcome, and persists the last few attempts. The app itself isn't blocked by the update — startup proceeds normally; the script is patched in place by the next time you trigger a search or a play.
The flow is gated by the Auto-update ani-cli setting (default ON). When it's off, the bundle just keeps using whatever script is in your cache, indefinitely. The latest update outcome is visible on the /diagnostics page.
A two-line summary: a Rust sidecar embedded inside an Electron shell speaks to Kitsu / AniList / aniskip and spawns ani-cli as a subprocess for stream resolution. A streaming proxy in the sidecar adds the right Referer: headers and rewrites HLS playlists so the embedded <video> element can play upstream content without CORS or referer issues. SQLite caches metadata; the filesystem caches images.
For the long version — diagrams, cache TTLs, the title-resolution bridge, the PiP architecture — see docs/architecture.md, docs/title-resolution.md, and the rest of docs/.
See docs/development.md. The repository carries one upstream patch (a single source-guard line in ani-cli for testability) and otherwise mirrors pystardust/ani-cli so script-side fixes flow in without conflict.
ani-gui only exists because of the projects it builds on:
- pystardust/ani-cli — the Bash scraper that does the actual stream resolution. ani-gui ships the script unmodified.
- Kitsu and AniList for the metadata, posters, and trending data behind the discovery surface.
- aniskip for the community-submitted OP/ED intervals.
- hls.js for the HLS playback inside the embedded player.
ani-gui is a tool. Like any tool, the responsibility for how it's used lies with the user. The app makes no claim on the content it surfaces — it talks to the same providers you'd reach in a browser and routes their output through your machine. The full project disclaimer applies: see disclaimer.md.
GPL-3.0, inheriting from upstream pystardust/ani-cli.

