Bryan Bailey d2c715e973 feat: add batcave.biz support, closes #6
## 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.
2026-03-11 20:55:03 -04:00
2024-08-26 22:49:26 -04:00

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

  1. Fetches the comic page and extracts the title and image links
  2. Downloads all pages concurrently with Cloudflare bypass
  3. Packages the images into a .cbz (Comic Book Zip) archive
  4. 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.

Yoink Web UI

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 .cbz archive 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, AZ, or ZA
  • One-click download — click any cover to download the .cbz archive directly

Packaging local images

Local packaging panel

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

Description
A tool for downloading comics from readallcomics.com and batcave.biz, packaging them as .cbz archives.
Readme 1,017 KiB
1.3.0 Latest
2026-03-12 14:27:01 +00:00
Languages
HTML 48.8%
Go 48.3%
Makefile 1.6%
Dockerfile 1.3%