## What changed - `BatcaveBizMarkup` now accepts a `clientChan chan *http.Client` and sends the authenticated cookie jar client back to the caller after completing the Cloudflare challenge flow. All error paths send nil so the caller never blocks. - `Comic` struct gains a `Client *http.Client` field. `NewComic` wires up the channel, receives the client, and stores it so downstream code can reuse the same authenticated session. - `downloadFile` branches on `c.Client`: when set it builds the request manually and only attaches a `Referer: https://batcave.biz/` header when the image URL is actually on batcave.biz. Some issues host images on third-party CDNs (e.g. readcomicsonline.ru) that actively block requests with a batcave Referer, returning 403 — omitting the header fixes those. - `ParseBatcaveBizTitle` extracts the chapter title from the `__DATA__.chapters` JSON array by matching the chapter ID in the URL's last path segment. The HTML `<title>` on batcave.biz is prefixed with "Read " and suffixed with "comics online for free", making it unsuitable as a filename. Using the chapter data gives clean titles like "Nightwing (1996) 153". "Issue #" and bare "#" are stripped since the hash character causes problems on some filesystems and tools. - `ParseBatcaveBizImageLinks` now unescapes `\/` → `/` in extracted URLs. The `__DATA__` JSON often contains forward-slash-escaped URLs that would otherwise be stored verbatim. - `archive.go`: `filepath.Walk` was called on `filepath.Dir(sourcePath)` (the library root) instead of `sourcePath` (the comic's own folder). This caused any leftover image files from previous downloads in sibling directories to be included in every new CBZ. Fixed by walking `sourcePath` directly. - `BatcaveBizMarkup` client now has a 30s `Timeout`. Without it, a single stalled CDN connection would hang the worker goroutine indefinitely, causing `Download()` to block forever waiting for a result that never arrives. - Fixed `for e := range err` in `cli/root.go` — ranging over `[]error` with one variable yields the index, not the error value.
yoink
A tool for downloading comics from readallcomics.com and packaging them as .cbz archives. Available as a CLI command or a self-hosted web application. The web UI also lets you package local image folders into .cbz archives directly from your browser.
How it works
- Fetches the comic page and extracts the title and image links
- Downloads all pages concurrently with Cloudflare bypass
- Packages the images into a
.cbz(Comic Book Zip) archive - Cleans up downloaded images, keeping only the cover (
001)
Installation
From source
Requires Go 1.22.3+:
go build -o yoink
Pre-built binaries
Pre-built binaries for Linux (arm64) and Windows are available on the releases page.
Docker
docker pull git.brizzle.dev/bryan/yoink-go:latest
CLI
Download a single comic issue:
yoink <url>
Example:
yoink https://readallcomics.com/ultraman-x-avengers-001-2024/
The comic title is extracted from the page and used to name the archive. Output is saved to:
<library>/<Title>/<Title>.cbz
Web UI
Yoink includes a self-hosted web interface for browsing and downloading comics from your browser.
Running directly
yoink serve
By default the server listens on port 8080. Use the -p flag to change it:
yoink serve -p 3000
Running with Docker
A docker-compose.yml is included for quick deployment:
docker compose up -d
Or with Podman:
podman compose up -d
The web UI is then available at http://localhost:8080.
Features
- Download queue — paste a comic URL into the input bar and track download progress in real time
- Local packaging — drag and drop a folder of images (or use the file picker) to package them as a
.cbzarchive and add it to your library without downloading anything - Library grid — browse your comics as a 150×300 cover grid with title-initial placeholders for missing covers
- Filter & sort — filter by title and sort by newest, oldest, A–Z, or Z–A
- One-click download — click any cover to download the
.cbzarchive directly
Packaging local images
Click the upload icon (↑) in the header to open the packaging panel. Enter a title, then either:
- Drag and drop a folder or image files onto the drop zone
- Select folder to pick an entire directory at once
- Select files to pick individual images
Images are sorted by filename, the first image is used as the cover, and the result is saved to your library as <Title>/<Title>.cbz.
Library volume
Downloaded comics are stored at the path set by YOINK_LIBRARY. When using Docker, mount this as a volume to persist your library across container restarts:
# docker-compose.yml
services:
yoink:
image: git.brizzle.dev/bryan/yoink-go:latest
ports:
- "8080:8080"
volumes:
- ./library:/library
environment:
- YOINK_LIBRARY=/library
restart: unless-stopped
Configuration
| Variable | Default | Description |
|---|---|---|
YOINK_LIBRARY |
~/.yoink |
Directory where comics are stored |
YOINK_LIBRARY=/mnt/media/comics yoink https://readallcomics.com/some-comic-001/
Dependencies
- goquery — HTML parsing
- cloudflare-bp-go — Cloudflare bypass
- cobra — CLI framework

