8.2 KiB
8.2 KiB
Project Plan: Anime (Rust GUI)
A cross-platform (Arch Linux + Windows) GUI application for searching, browsing, and watching anime. Inspired by ani-cli.
Features
Core (MVP)
- Search anime by name via the AllAnime GraphQL API
- Display search results in a list
- Select an anime and view its available episodes
- Select an episode to play in the OS default media player
- Watch history -- track what you've watched, resume from where you left off
Extended
- Download episodes (mp4 direct download via reqwest, m3u8 via yt-dlp/ffmpeg subprocess)
- Download progress bar
- Sub/Dub toggle
- Quality selection
- Episode range selection for batch downloads
Tech Stack
GUI Framework
egui / eframe
- Immediate-mode GPU-rendered GUI
- Single binary, no runtime dependencies
- Native look on both Linux and Windows
- Easy to iterate on, good for list-based UIs
Crates
| Crate | Version | Purpose |
|---|---|---|
eframe |
latest | Window management, OpenGL/wgpu backend for egui |
egui |
latest | Immediate-mode UI widgets (comes with eframe) |
reqwest |
latest | HTTP client for GraphQL API calls and mp4 downloads |
tokio |
latest | Async runtime (required by reqwest, keeps GUI responsive) |
serde |
latest | Serialization framework |
serde_json |
latest | JSON parsing for GraphQL responses |
open |
latest | Open video URL in OS default player (xdg-open on Linux, start on Windows) |
directories |
latest | Cross-platform paths for config/state (history file) |
anyhow |
latest | Ergonomic error handling with context |
indicatif |
latest | Progress bars for downloads (can integrate into egui) |
Standard Library (no extra crate needed)
| Module | Purpose |
|---|---|
std::process::Command |
Shell out to yt-dlp / ffmpeg for m3u8 downloads |
std::fs |
Read/write history file |
External Tools (runtime, not compile-time)
| Tool | Required? | Purpose |
|---|---|---|
mpv / vlc / system default |
Yes (any player) | Video playback |
yt-dlp |
For m3u8 downloads | HLS stream downloading |
ffmpeg |
Fallback for m3u8 | HLS stream downloading (if yt-dlp unavailable) |
Architecture
src/
├── main.rs -- App entry, eframe::run_native() setup
├── app.rs -- Main App struct implementing eframe::App
│ Holds app state, routes between views
├── views/
│ ├── mod.rs
│ ├── search.rs -- Search input + results list view
│ ├── episodes.rs -- Episode list view for a selected anime
│ └── player.rs -- "Now playing" view (current episode, next/prev controls)
├── api/
│ ├── mod.rs
│ ├── client.rs -- Shared reqwest::Client setup (user-agent, referer)
│ ├── search.rs -- search_anime() -- GraphQL search query
│ ├── episodes.rs -- episodes_list() -- GraphQL episodes query
│ ├── sources.rs -- get_episode_url() -- GraphQL embed query + provider decoding
│ ├── providers.rs -- Provider-specific link extraction (wixmp, hianime, etc.)
│ └── cipher.rs -- Hex substitution cipher for decoding provider URLs
├── download.rs -- Download logic: direct mp4 via reqwest, m3u8 via subprocess
├── history.rs -- Read/write watch history to local state file
└── player.rs -- Launch video in OS default player via `open` crate
Data Flow
┌──────────────────────────────────────────────────────────────┐
│ GUI (egui) │
│ │
│ ┌─────────┐ ┌────────────┐ ┌────────────────────┐ │
│ │ Search │───>│ Episodes │───>│ Play / Download │ │
│ │ View │ │ View │ │ View │ │
│ └─────────┘ └────────────┘ └────────────────────┘ │
│ │ │ │ │ │
└───────┼───────────────┼──────────────────┼─────────┼────────┘
│ │ │ │
v v v v
search_anime() episodes_list() get_episode_url()│
│ │ │ │
└───────┬───────┘ │ ┌────┘
v v v
AllAnime GraphQL API ┌──────────────┐
(api.allanime.day) │ Playback │
├──────────────┤
│ open crate │ -> OS default player
│ reqwest │ -> direct mp4 download
│ yt-dlp/ffmpeg│ -> m3u8 download
└──────────────┘
API Details
See ani-cli.md for full documentation of:
- GraphQL queries and variables
- Hex substitution cipher mapping
- Provider-specific link extraction
- Video source formats (mp4 vs m3u8)
Key Constants
const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0";
const ALLANIME_REFERER: &str = "https://allmanga.to";
const ALLANIME_API: &str = "https://api.allanime.day";
const ALLANIME_BASE: &str = "allanime.day";
History File
- Linux:
~/.local/state/anime/history.json - Windows:
C:\Users\<user>\AppData\Local\anime\state\history.json - Resolved via the
directoriescrate (ProjectDirs::from("", "", "anime"))
Format (JSON for easy serde):
[
{
"show_id": "abc123",
"title": "Cyberpunk Edgerunners",
"episode": "5",
"mode": "sub",
"last_watched": "2026-04-14T12:00:00Z"
}
]
Download Strategy
Direct MP4 links
- Use
reqwestto stream the response body to a file - Show progress via content-length header + bytes received
- No external tools needed
m3u8 / HLS streams
- Shell out to
yt-dlp(preferred) orffmpeg(fallback) viastd::process::Command - Pass referrer header as argument
- Capture stdout/stderr for progress reporting
- This avoids reimplementing HLS segment fetching, concatenation, and remuxing
Download directory
- Default: current working directory (or configurable)
- File naming:
<anime_title> Episode <number>.mp4
Build Targets
| Platform | Target Triple | Notes |
|---|---|---|
| Arch Linux | x86_64-unknown-linux-gnu |
Primary dev target |
| Windows | x86_64-pc-windows-msvc |
Cross-platform support |
Both are tier 1 Rust targets with full support.
Implementation Order
- Project setup -- Add dependencies to
Cargo.toml, basic eframe window - API client -- reqwest client with correct headers, test connectivity
- Cipher -- Implement hex substitution decoder
- Search -- GraphQL search query, parse results
- Episodes -- GraphQL episode list query, parse results
- Sources -- GraphQL embed query, decode providers, extract video links
- Playback -- Open video URL in default player via
opencrate - GUI: Search view -- Text input, results list, selection
- GUI: Episodes view -- Episode list, selection, play button
- GUI: Now playing view -- Current episode info, next/prev/replay controls
- History -- Read/write history file, continue watching flow
- Downloads -- Direct mp4 download, m3u8 via subprocess, progress reporting
- Polish -- Error handling, loading states, quality selection, sub/dub toggle