Terminal tool for batch-downloading and streaming anime from AnimePahe. Features a parallel HLS engine with segment-level crash recovery, Rich-powered live dashboard, and MPV streaming with mid-playback SUB/DUB switching.
- About
- Prerequisites
- Installation
- Quick Start
- Feature Tour
- CLI Reference
- Architecture
- Package Structure
- Development
- Troubleshooting
- Contributing
- License
Pahebatcher is a terminal application for batch-downloading anime from AnimePahe. It supports parallel episode downloads with per-episode HLS segment concurrency, segment-level crash recovery, interactive episode selection, and MPV streaming with mid-playback audio track switching.
Key characteristics:
- Single-site focus. Pahebatcher targets AnimePahe exclusively and does not support other sources.
- Self-hosted infrastructure. Cloudflare bypass uses a local FlareSolverr instance via Docker. All traffic stays on your machine — no third-party proxies. You can also inject your browser's
cf_clearancecookie to skip challenge solving entirely. - Atomic segment writes. HLS segments are written to
.tmpand renamed to.tsafter completion. A mid-download interruption picks up at the exact segment where it left off, without re-downloading completed work. - Concurrent pipeline. A two-stage prefetch architecture resolves stream URLs ahead of downloaders via an
asyncio.Queue. A configurableresolve_aheadthrottle controls how many episodes the resolver stays ahead of downloaders to avoid Cloudflare request bursts. Episodes download in parallel (configurable 1–6), with per-episode segment concurrency (configurable 8–32). - Rich terminal UI. Progress dashboard shows all episodes simultaneously with per-episode segment counts, transfer speeds, ETAs, file sizes, and color-coded state transitions. Interactive episode selection includes range input, a toggle checklist, and "latest N" mode.
- MPV streaming. Episodes can be streamed without downloading. A playback panel shows the current episode and playlist position. Audio tracks can be toggled between SUB and DUB mid-session.
- Session management. Previous download sessions can be resumed, deleted, or cleared from the cache. Cached segments are reused on restart. Scan results are cached to disk with a configurable TTL so re-running the tool skips all AnimePahe API calls for fresh data.
- Persistent configuration. Quality, audio, concurrency, and output directory are saved to
pahebatcher.tomlin the project directory. Set once via the interactive wizard orpahebatcher config set, reused on every subsequent run. CLI flags override persisted values when needed. Edit the file directly or use theconfigsubcommand. - MIT licensed. Free to use, modify, and redistribute.
| Requirement | Purpose | Install |
|---|---|---|
| FlareSolverr | Cloudflare bypass (headless Chromium) | docker run -d --name=flaresolverr -p 8191:8191 ghcr.io/flaresolverr/flaresolverr |
| FFmpeg | TS segment concatenation into MP4 | sudo apt install ffmpeg (Linux) / brew install ffmpeg (macOS) |
| MPV | Streaming mode only | sudo apt install mpv / brew install mpv |
| Python 3.11+ | Runtime | python3 --version |
FlareSolverr must be running before pahebatcher starts. The tool checks reachability on launch and prints the exact Docker command if it cannot connect. The default URL is http://localhost:8191/v1; override with the FLARESOLVERR_URL environment variable.
git clone https://github.com/smolfiddle/pahebatcher.git
cd pahebatchermake run # interactive wizard
make run "URL" # skip search, go directly to series
make help # show all targets
make config-show # display current settings
make watchlist-list # list watchlist
make watchlist-check # check watchlist for new episodes
make test # run all 203 tests
make lint # ruff check (0 errors)
make typecheck # mypy strict (0 errors)
make benchmark # full coherence benchmark
make help # show all targetsAfter first run, set persistent defaults:
make config-show
venv/bin/python -m pahebatcher config set quality 720
venv/bin/python -m pahebatcher config set audio_lang engPass a URL directly (positional or via URL=):
make run "https://animepahe.pw/anime/<uuid>"
make run URL="https://animepahe.pw/anime/<uuid>" ARGS="--all -q 720"pipx install .
pahebatcherpipx installs pahebatcher into an isolated venv at ~/.local/share/pipx/venvs/pahebatcher and symlinks ~/.local/bin/pahebatcher. That venv is not auto-updated when you git pull / git checkout.
Updating after git pull or switching branches (e.g. feature/watchlist):
# from the repo root, after git pull / git checkout:
pipx install . --force && hash -r
pahebatcher --help | grep watchlist # should list watchlist commands
# alternative (same effect):
pipx reinstall pahebatcherIf you skip this, pahebatcher watchlist check will say unrecognized arguments: check (you're still running the old 3.0.0 binary) while make watchlist-check works (it uses venv).
pip install -e ".[dev]"
pahebatcher
# no --force needed: edits to src/ are live; only reinstall if pyproject.toml changesNote: The developer has never tested pahebatcher on actual Windows. The Python code is OS-neutral (
pathlib/asyncio,store.py:40atomic.tmp→.ts,pyproject.toml:8Operating System :: OS Independent), but Windows has only been reasoned about, not run. WSL2 is the safer bet if you hit edge cases.
Prerequisites (Windows):
| Requirement | Install (Windows) |
|---|---|
| Python 3.11+ | winget install Python.Python.3.12 or https://www.python.org/downloads/ — check py -3.11 --version |
| FlareSolverr | Docker Desktop: docker run -d --name=flaresolverr -p 8191:8191 ghcr.io/flaresolverr/flaresolverr — keep http://localhost:8191/v1 (or set FLARESOLVERR_URL) |
| FFmpeg | winget install Gyan.FFmpeg or https://ffmpeg.org/download.html — ensure ffmpeg -version is on PATH |
| MPV (stream only) | winget install mpv.net or https://mpv.io/install/ — ensure mpv --version is on PATH |
Install & run:
git clone https://github.com/smolfiddle/pahebatcher.git
cd pahebatcher
py -3.11 -m venv venv
venv\Scripts\python -m pip install -e ".[dev]"
venv\Scripts\python -m pahebatcher --help
venv\Scripts\python -m pahebatcher watchlist add https://animepahe.pw/anime/<uuid> -q 720
venv\Scripts\python -m pahebatcher wl check
# pipx alternative (same isolated-venv caveat as Linux):
# pipx install .
# pahebatcher --help | findstr watchlistWindows differences:
- No
make— usevenv\Scripts\python -m pahebatcher ...directly (Makefile:42is Unix-only). - No
cron— use Task Scheduler: Create Task → Trigger daily/hourly → ActionStart a program→ ProgramC:\path\to\pahebatcher\venv\Scripts\python.exe→ Arguments-m pahebatcher wl check→ Start inC:\path\to\pahebatcher(sowatchlist.json/pahe_cacheresolve,WATCHLIST_PATHalso works). - Paths use
.\andvenv\Scripts\on Windows vsvenv/bin/on Linux/macOS.
If you hit Windows-specific issues (long paths, sanitize reserved names, console encoding), please open an issue mentioning Windows.
All three methods produce the pahebatcher command. You can also run via python -m pahebatcher.
Which binary am I running?
which -a pahebatcher # pipx → ~/.local/bin/pahebatcher, venv → ./venv/bin/pahebatcher pahebatcher --help | grep watchlist # global: should list watchlist if up-to-date make watchlist-list # always uses venv → correct for your checkout venv/bin/pahebatcher watchlist list # direct venv binary venv/bin/python -m pahebatcher watchlist list # most explicit, never staleIf global is stale, use the
venv/makeform or refresh pipx as above.
# Interactive wizard -- search for a series or paste a URL
pahebatcher
# Download entire series, 720p, Japanese audio, 2 concurrent episodes
pahebatcher "https://animepahe.pw/anime/<uuid>" --all -q 720
# Download episodes 1 through 12, English dub, 1080p, custom output directory
pahebatcher "https://animepahe.pw/anime/<uuid>" --range 1-12 --audio eng -q 1080 -o ~/anime
# Download only the 3 most recently aired episodes
pahebatcher "https://animepahe.pw/anime/<uuid>" --latest 3
# List all episodes and exit (no download)
pahebatcher "https://animepahe.pw/anime/<uuid>" --list
# Stream episodes in MPV with on-the-fly SUB/DUB switching
pahebatcher "https://animepahe.pw/anime/<uuid>" --stream -q 1080
# 4 concurrent episodes, 32 HLS workers per episode
pahebatcher "https://animepahe.pw/anime/<uuid>" --all -q 1080 -j 4 -w 32
# Save default preferences so you don't need flags every time
pahebatcher config set quality 720
# Watchlist — follow a weekly airing show without re-running manually
pahebatcher watchlist add https://animepahe.pw/anime/<uuid> -q 1080 --audio jpn -o ~/anime
pahebatcher wl list # wl/w = shorthand for watchlist
pahebatcher check # shorthand for watchlist check (also: wl check, wl c, sync)
# Enable debug logging for troubleshooting
pahebatcher "https://animepahe.pw/anime/<uuid>" --all --verboseOutput files are saved as Ep 001 - Episode Title.mp4 in the output directory (default: ./downloads/<series_name>/).
Watchlist state lives at watchlist.json (cwd, git-ignored). Delete it or watchlist remove to stop tracking. Override location with WATCHLIST_PATH=/tmp/my.json.
The core workflow: scan a series, select episodes, configure settings, download.
Episode selection offers five modes accessible from the interactive wizard or CLI flags:
| Mode | CLI flag | Interactive | Description |
|---|---|---|---|
| All | --all |
Press A |
Download every episode in the series |
| Range | --range 1-12 |
Press R |
Specify with 1-12, 1,4,7, 13- (open-ended), or mixed 1-6,10,14- |
| Toggle checklist | — | Press L |
Interactive table; toggle individual episodes with numbers, a=select all, n=deselect all, done=confirm |
| Latest N | --latest 3 |
Press N |
Grab the most recent N episodes |
| Skip | — | Press S |
Return to action menu without selecting |
Settings wizard (interactive mode only) prompts for quality, audio language, output directory, and concurrency. Choices are automatically persisted to pahebatcher.toml and reused on future runs — run the wizard once, no need to reconfigure on subsequent sessions.
Download dashboard shows every episode simultaneously with live per-episode metrics: segment counter (M of N), percentage, transfer speed, ETA, and file size. Each episode transitions through color-coded states: resolving (cyan) -> queued (dim cyan) -> downloading (bold white) -> remuxing (yellow) -> done (green checkmark) / fail (red cross).
Segment-level crash recovery. HLS segments are written atomically (.tmp file renamed to .ts after write completes). On restart, the tool reads done_indices() and only fetches missing segments. Already-completed MP4 files in the output directory are skipped entirely.
Launches MPV with the resolved M3U8 URL and authentication headers. Displays a live "Now Playing" panel with episode title, audio track, quality, and playlist position.
Post-episode navigation:
N/P— next / previous episode in playlistA— toggle SUB / DUB audio track mid-session (reloads playlist with new audio lane)R— replay current episodeS— jump to any episode by numberQ— quit
Post-episode controls appear after MPV closes, offering navigation, audio switching, replay, and episode selection.
Accessible from the main menu (option 3). Lists all cached sessions with:
- Anime title and URL
- Episode count and segment count
- Total cache size on disk
- Status (Paused)
Actions: Resume (restarts tool with that series URL), Delete (removes single session cache), Clear All (wipes entire pahe_cache/ directory).
Running pahebatcher without a URL opens interactive search. Type an anime title, browse results in a table (title, type, year, episodes, score), select by number. The tool auto-discovers SUB and DUB variants of the selected series.
Track weekly airing shows without re-running the tool manually. Add a series once with your preferred quality/audio/output, then run a single check command (e.g. from cron) — it scans every watched anime, downloads only episodes not already on disk, resumes partial downloads via the existing segment store, and is safe to run repeatedly.
Workflow:
addvalidates the AnimePahe URL (scanner.py:30), normalizes tohttps://<host>/anime/<uuid>, and saves prefs. Without flags it inheritspahebatcher.toml(config_manager.py:15).list/show/remove/resetmanage state.show/remove/resetaccept1-based index, full URL, session UUID, or title substring (watchlist.py:103).checkis one-shot: for each entry itscans withcache_ttl=0(forces fresh, unlike normal60min cache), diffs against files on disk viaBatchOrchestrator._find_existing()(downloader.py:154—Ep 001/Ep_001prefix, size>0), plus remembers deleted episodes while the series folder exists (downloadedhistorywatchlist.py:30), and reuses the 2-stage pipeline. Already-present MP4s are skipped, partial.tssegments are resumed. Running it again immediately downloads nothing.
# Add a series to the watchlist (mirrors download flags) — long or short
pahebatcher watchlist add https://animepahe.pw/anime/<uuid> -q 1080 --audio jpn -o ~/anime
pahebatcher wl add https://animepahe.pw/anime/<uuid> --audio eng -q 720 -j 2 -w 24 --keep-temp --retry 2 # wl/w = shorthand
pahebatcher w add https://animepahe.pw/anime/<uuid> # uses pahebatcher.toml defaults
# List / inspect / remove / reset / relink ( --yes skips confirmation)
pahebatcher watchlist list # or wl list, wl ls, wl l
pahebatcher wl show 1 # s/info also work: wl s 1
pahebatcher wl show https://animepahe.pw/anime/<uuid>
pahebatcher wl remove 1 --yes # also rm/r/del
pahebatcher wl reset 1 # also rst/clear — clear deleted-history
pahebatcher wl relink 1 https://animepahe.pw/anime/<new-uuid> # fix one dead link
pahebatcher wl relink 1 # auto — finds new UUID by title (no URL needed)
pahebatcher wl relink # one command — auto-relinks ALL dead entries
pahebatcher wl relink # one command — auto-relinks ALL dead entries
# Check for new episodes and download (one-shot, cron-friendly)
pahebatcher watchlist check
pahebatcher wl check # same
pahebatcher wl c # shortest: wl c
pahebatcher check # top-level shorthand → watchlist check
pahebatcher sync # alias for check
pahebatcher wl check --verbose
pahebatcher wl check https://animepahe.pw/anime/<uuid> # single series
# Make wrappers (same as above, use project venv)
make watchlist-list # or make run ARGS="wl ls"
make watchlist-check # or make run ARGS="check" / ARGS="wl c"State & persistence:
- File:
watchlist.jsonincwd(sibling topahebatcher.toml), git-ignored, survivespahe_cacheclear and restarts. Atomic write via.tmp→rename(watchlist.py:98). Corrupted JSON is treated as empty. - Env:
WATCHLIST_PATH=/tmp/custom.json pahebatcher watchlist listoverrides location (useful for tests/cron isolation). - Entry fields:
url,session,host,title(auto-filled oncheck),quality,audio_lang,output_dir(base, sanitized title appended asos.path.join(output_dir, sanitize(title))),max_parallel,hls_workers,keep_temp,auto_retry,added_at,last_checked,downloaded(list of episode numbers ever seen on disk). - Title is placeholder
sessiononaddand refreshed on firstcheckviascan. Updating an existing URL keepsadded_atand preserves a real title anddownloadedhistory.
Cron / systemd (one-shot only — no daemon):
# hourly, append logs
0 * * * * cd /path/to/pahebatcher && venv/bin/python -m pahebatcher watchlist check >> watchlist.log 2>&1
# or with make
0 * * * * cd /path/to/pahebatcher && make watchlist-check >> watchlist.log 2>&1
# custom state location
0 * * * * WATCHLIST_PATH=/home/user/.config/pahebatcher/watchlist.json /home/user/pahebatcher/venv/bin/pahebatcher watchlist check >> /tmp/watchlist.log 2>&1Idempotency: second check immediately is a no-op. Failures are per-series isolated — one
0 comments
log in to comment.