No description
  • TypeScript 98.5%
  • Python 1.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Yumeo 28674da73d
All checks were successful
Build and Release / build (push) Successful in 18m19s
chore: bump version to 0.6.0
2026-10-30 22:13:28 +01:00
.forgejo/workflows fix: wrong extension build command 2026-10-08 22:48:50 +02:00
.vscode chore: update dependencies 2026-06-14 14:06:53 +02:00
docker feat: jxl migration 2026-10-30 22:05:02 +01:00
scripts feat: script to remove manga from queue 2026-10-30 22:12:55 +01:00
src feat: jxl migration 2026-10-30 22:05:02 +01:00
.dockerignore fix: wrong extension build command 2026-10-08 22:48:50 +02:00
.env.example feat: jxl migration 2026-10-30 22:05:02 +01:00
.gitignore chore: clean up backend codebase 2026-10-08 20:27:48 +02:00
.npmrc fix: proxy implementation 2026-05-23 20:25:02 +02:00
AGENTS.md chore: clean up backend codebase 2026-10-08 20:27:48 +02:00
biome.json misc: remove extension, only keep userscript 2026-10-08 19:34:19 +02:00
bun.lock chore: clean up dead code 2026-10-08 20:35:17 +02:00
package.json chore: bump version to 0.6.0 2026-10-30 22:13:28 +01:00
prisma.config.ts refactor workspace 2026-06-29 18:29:37 +02:00
pyproject.toml feat: jxl migration 2026-10-30 22:05:02 +01:00
README.md feat: rocm 2026-10-08 18:31:53 +02:00
tsconfig.json chore: clean up userscript codebase 2026-10-08 20:18:24 +02:00
uv.lock feat: jxl migration 2026-10-30 22:05:02 +01:00

nmanga-list

To install dependencies:

bun install

To run:

bun run index.ts

This project was created using bun init in bun v1.3.12. Bun is a fast all-in-one JavaScript runtime.

Resource limits for large manga

Large manga can use a lot of RAM/CPU because image conversion + embedding run in worker threads.

Docker limits

docker/docker-compose.yml now includes an app service with hard caps:

  • cpus: 2
  • mem_limit: 2g

Tune those values based on your machine. Example:

app:
	cpus: 1.5
	mem_limit: 1536m

App-level throttles

Use these environment variables to reduce workload:

  • IMAGE_QUEUE_WORKERS (default: 1)
    Number of manga processed at same time.
  • IMAGE_PAGE_CONCURRENCY (default: 2)
    Number of pages fetched/converted at same time per worker.
  • MANGA_REFRESH_INTERVAL_HOURS (default: 2)
    Minimum hours before /api/scrape re-fetches manga metadata/tags again. Within this interval, scrape request returns fresh and skips re-fetch.

Safe low-memory profile:

IMAGE_QUEUE_WORKERS=1
IMAGE_PAGE_CONCURRENCY=1

nhentai 429 handling with temporary proxies

When nhentai responds with HTTP 429, the app now:

  • Reads Retry-After when available (falls back to 60s, capped at 10m)
  • Shares one cooldown across all concurrent workers in the same app process
  • Uses rotating proxies only while that cooldown is active

Proxy list is loaded once on app startup from ProxyScrape by default.

Optional env vars:

  • NMANGA_NHENTAI_PROXY_ENABLED (default: true)
    Set to false to disable proxy usage entirely.
  • NMANGA_NHENTAI_PROXY_LIST_URL (default: ProxyScrape elite list URL)
    Override with your own provider URL that returns one proxy per line in protocol://ip:port format.
  • NMANGA_NHENTAI_FETCH_TIMEOUT_MS (default: 15000)
    Per-request timeout for nhentai direct/proxy fetches. Increase if your network/proxy path is slow; decrease to fail over faster.

Remote worker via CLI

You can run additional queue workers on another machine using the same database and S3/MinIO bucket:

bun run cli remote-worker

By default this command starts a worker subprocess with:

  • env file: .env.remote
  • concurrency: 1
  • poll interval: 1000ms

Examples:

# 2 worker loops
bun run cli remote-worker --concurrency 2

# custom env file
bun run cli remote-worker --env-file /etc/nmanga/worker.env --concurrency 2

.env.remote must contain the server connection settings — not just the API URLs — because the Python embedding worker on the same machine connects to the database and bucket directly:

API_URL=http://10.0.0.11:3006
API_WS_URL=ws://10.0.0.11:3008/trpc
DATABASE_URL=postgresql://nmanga:CHANGE_ME@10.0.0.11:5432/nmanga?schema=public
MINIO_ENDPOINT=10.0.0.11
MINIO_PORT=9000
MINIO_USE_SSL=false
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_BUCKET=nmanga

Real environment variables take precedence; with bun --env-file the root .env is not loaded, and the Python workers read .env.remote themselves (falling back to .env for keys missing there).

All normal scraper/proxy env vars are supported, including:

  • NMANGA_NHENTAI_PROXY_ENABLED
  • NMANGA_NHENTAI_PROXY_LIST_URL
  • NMANGA_NHENTAI_FETCH_TIMEOUT_MS

AMD GPU (ROCm) embedding worker

On Windows, uv sync installs ROCm-enabled PyTorch from AMD's index (torch 2.12.0+rocm10.1.0 for the Radeon RX 9070/XT, target device-gfx1201) instead of the CPU wheel. Other platforms (Docker/Linux) keep resolving regular PyPI wheels. The first sync downloads roughly 1.4 GB (ROCm runtime).

Requirements:

  • Latest AMD Adrenalin driver
  • Python 3.13 / 3.14 (managed by uv)

Verify the GPU is visible:

uv run python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0), torch.version.hip)"

Embedding workers use the GPU by default (--device cuda); rocm and hip are accepted as aliases:

uv run scripts/embed_worker.py --once   # continuous DB embedding worker
uv run scripts/reembed_images_gpu.py    # re-embed stored pages

For a different AMD GPU, change the device-gfx1201 extras on the torch/torchvision dependencies in pyproject.toml to the matching target (e.g. device-gfx1100 for the RX 7900 XTX), then run uv lock && uv sync.

If inference crashes or resets the GPU on RX 9070 series, run with TORCH_BLAS_PREFER_HIPBLASLT=0 to disable hipBLASLt.

Nginx Proxy Manager setup

This app serves HTTP API on port 3000 and tRPC WebSocket on port 3001.

In Nginx Proxy Manager, create a Proxy Host:

  • Domain Names: nmanga.domain.dev
  • Scheme: http
  • Forward Hostname / IP: your app container or host (example: app)
  • Forward Port: 3000
  • Websockets Support: enabled

Then add this in Advanced:

location /trpc {
	set $nmanga_upstream http://app:3000;
	if ($http_upgrade = "websocket") {
		set $nmanga_upstream http://app:3001;
	}

	proxy_pass $nmanga_upstream;
	proxy_http_version 1.1;
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Forwarded-Proto $scheme;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";
	proxy_read_timeout 600s;
}

Replace app with your actual upstream hostname/IP used by NPM.