OPDS-PSE 串流服务器,作为 E-Hentai 的中转代理:输出 OPDS 1.2(Atom) 与 OPDS 2.0(JSON) 双版本目录 + PSE 串流链接,适用于各种支持 OPDS-PSE 流式传输的阅读器。
本仓库仅包含中转代理逻辑,不托管任何内容。
镜像由 GitHub Actions 自动构建发布至 ghcr.io/niatsysor/pandaopds(多架构 linux/amd64 + linux/arm64,树莓派/NAS 直接使用)。免构建启动:
登录 e-hentai.org,从浏览器 cookie 取 ipb_member_id / ipb_pass_hash 填入:
docker run -d --name pandaopds --restart unless-stopped \
-p 127.0.0.1:8000:8000 \
-e IPB_MEMBER_ID=<your_id> \
-e IPB_PASS_HASH=<your_hash> \
-e CACHE_DIR=/data/cache \
-e ARCHIVE_DIR=/data/archives \
-v pandaopds-cache:/data/cache \
-v pandaopds-archives:/data/archives \
ghcr.io/niatsysor/pandaopds:latest可选参数:-e EH_SITE=exhentai、-e PUBLIC_BASE_URL=https://opds.example.com(反代下输出绝对 URL)、-e AUTH_USERNAME=xxx -e AUTH_PASSWORD=xxx(两者都设置才启用 Basic Auth)、-v ./config:/config(自定义 OPDS 2.0 首页布局)。
git clone https://github.com/<your-name>/PandaOPDS.git
cd PandaOPDS
cp .env.example .env # 可选:按需填写 IPB_MEMBER_ID / IPB_PASS_HASH(不复制也能直接启动,全默认)
# 高级可选配置见 .env.example 注释 —— compose 经 env_file 把 .env **全部变量**批量透传进容器,
# 无需在 docker-compose.yml 逐项映射;`.env` 文件缺失或为空也能正常启动(公开内容 + 默认配置),
# 容器内 CACHE_DIR/ARCHIVE_DIR 固定指向持久卷,限流预设由镜像内置
# 自定义 env 路径:
# PANDAPDS_ENV_FILE=/path/mine.env docker compose --env-file=/path/mine.env up -d
# (两个参数须指向同一文件,保证 environment 插值与 env_file 同源)
docker compose up -d # 直接拉取预构建镜像,不构建
# 升级:docker compose pull && docker compose up -d
# 固定版本:发布 tag(如 v0.2.0)存在后,把 image 改为 ghcr.io/niatsysor/pandaopds:v0.2.0docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build- OPDS 2.0 首页布局:编辑
config/home.toml(参照config/home.toml.example),声明分组与区块;不配置时使用内置默认布局。 - 分类筛选(facets):
FACETS环境变量,格式名称:排除掩码,逗号分隔(如FACETS=纯本子:1021,漫画:1019)。 - 不提供 IPB cookie:服务照常运行,公开内容(Latest / Popular / Toplist / Search)可用,仅 Watched / Favorites 导航项不输出。
内置一个轻量管理界面,用于查看当前配置与运行状态(当前阶段只读,编辑能力后续迭代):
| 路由 | 说明 |
|---|---|
GET / |
单页界面:仪表盘(状态/熔断器/请求计数/缓存)+ 环境变量配置 + 首页布局(挂载于根目录) |
GET /api/status |
JSON:服务状态、熔断器、节流计数、缓存统计、首页来源 |
GET /api/config |
JSON:全量生效配置(分组),凭据类字段服务端脱敏 |
GET /api/home |
JSON:home.toml 布局(groups/sections、来源标记、解析错误) |
GET /api/archive |
JSON:归档列表 + 统计 |
GET /api/archive/{gid}/{token}/quote |
归档报价(标题/画质/GP 价格,不扣 GP) |
POST /api/archive/{gid}/{token}/start |
购买并开始归档任务(消耗 GP;已购直接重下) |
GET/DELETE /api/archive/{gid}/{token} |
单条状态 / 删除本地归档 |
POST /api/archive/{gid}/{token}/refresh |
重新下载(不扣 GP) |
-
归档(Archiver):WebUI 的「归档」视图通过上述 API 管理 GP 购买的持久归档——输入图库 URL 查询报价 → 选画质确认 → 后台下载统一保存为 zip(cbz)母本于
ARCHIVE_DIR,/stream对该图库直接读归档页(长效缓存,不受磁盘 LRU/7 天 TTL 约束)。需登录态与星会员;未解锁档消耗 GP,请确认后操作。 -
前端为单 HTML(内联 CSS/JS,无构建链、无 CDN 依赖),消费上述 JSON API;未来功能(离线项目管理、自动化工作流)扩展 API 层即可,页面契约不变。
-
安全:
IPB_PASS_HASH/IGNEOUS永不回传明文(页面与 API 均只显示占位符)。IPB_MEMBER_ID为登录标识,会完整展示。 -
WebUI 不触达 E-Hentai,仅读取内存状态;服务配置异常时页面照常可访问并显示错误详情。
-
可选 Basic Auth:设置
AUTH_USERNAME+AUTH_PASSWORD后,除/health(及AUTH_EXEMPT_PATHS指定路径、AUTH_EXEMPT_PREFIXES路径前缀)外全部路由需 Basic 凭据,WebUI 同样受保护;未配置时保持默认公开(docker-compose 绑定 loopback / 反代控制访问)。Web 阅读器渲染评论封面等<img>原生加载场景建议AUTH_EXEMPT_PATHS=/image/fetch(见下)。
| 变量 | 默认 | 说明 |
|---|---|---|
IPB_MEMBER_ID |
空 | 登录 cookie(可选:不填则仅公开内容,Watched/Favorites 不可用) |
IPB_PASS_HASH |
空 | 登录 cookie(与 IPB_MEMBER_ID 成对提供) |
EH_SITE |
e-hentai |
e-hentai | exhentai |
IGNEOUS |
空 | 可选会话种子;exhentai 会话建立时自动下发,无需用户提供 |
NW |
1 |
绕过 Offensive For Everyone 警告 |
DATATAGS |
1 |
启用新缩略图结构 |
PUBLIC_BASE_URL |
空 | 设置后 feed 输出绝对 URL,如 https://opds.example.com |
CACHE_DIR |
./cache |
图片磁盘缓存目录 |
CACHE_MAX_GB |
4 |
磁盘缓存上限(GB) |
IMAGE_CACHE_ENABLED |
true |
设为 false 关闭磁盘缓存 |
ARCHIVE_DIR |
./archives |
归档持久目录(zip 母本 + meta.json;建议独立卷) |
ARCHIVE_QUALITY |
original |
默认画质档(start 未传 quality 时) |
ARCHIVE_DOWNLOAD_CONCURRENCY |
5 |
归档下载/7z 转换并发上限,其余排队 |
HTML_INTERVAL_SECONDS |
0.3 |
HTML 出站请求最小间隔(秒),防封关键;compose/Docker 镜像推荐保守值 1.5(单用户自用可安全尝试 0.3,在 .env 设置) |
MAX_CONCURRENCY |
5 |
HTML/API 出站并发上限(防封关键路径);compose/Docker 镜像推荐保守值 2(单用户自用可安全尝试 5) |
IMAGE_MAX_CONCURRENCY |
5 |
全图(/stream 原图)并发上限:509 配额流量,保守 |
THUMB_MAX_CONCURRENCY |
25 |
封面图/缩略图(ehgt CDN)并发上限:对齐浏览器在源站 25 缩略图并发 |
TIMEOUT_SECONDS |
6 |
出站请求超时(秒) |
RETRIES |
3 |
网络错误重试次数 |
BANNED_COOLDOWN_SECONDS |
1800 |
IP 封禁熔断冷却(秒) |
EXCEED_COOLDOWN_SECONDS |
300 |
图片限额熔断冷却(秒) |
PSE_PAGE_BASE |
1 |
PSE 页码基数:1(1-based,默认)或 0(OPDS-PSE 规范原文 0-based) |
TAG_STATUS_FILTER |
balanced |
标签可信度过滤:balanced(默认,confidence+skepticism)、strict(仅 confidence)、off(全部保留) |
TAG_TRANSLATION_ENABLED |
0 |
中文标签翻译(EhTagTranslation/Database 词典):1 启用后 OPDS 2.0 subject 输出中文译名、搜索支持中文标签反查;关闭时零行为变化 |
TAG_TRANSLATION_URL |
release latest db.text.json |
词典下载地址,可指向自镜像 |
TAG_TRANSLATION_INTERVAL_SECONDS |
86400 |
后台刷新间隔(秒);0 = 仅启动时拉取一次 |
TAG_TRANSLATION_STATE |
./tag_translation.json |
磁盘快照路径(删除后下次刷新重新下载) |
FACETS |
内置 10 分类 | OPDS 2.0 分类筛选,格式 名称:掩码,名称:掩码 |
EH_PROFILE |
PandaOPDS |
E-Hentai uconfig 独立 profile 名;设空串关闭 |
HOME_CONFIG |
./config/home.toml |
OPDS 2.0 首页布局配置文件路径 |
AUTH_USERNAME |
空 | 可选 Basic Auth 用户名;与 AUTH_PASSWORD 同时设置才启用 |
AUTH_PASSWORD |
空 | 可选 Basic Auth 密码(明文,脱敏显示;仅建议 HTTPS 反代下启用) |
AUTH_EXEMPT_PATHS |
空 | 逗号分隔的精确路径,认证下仍公开;/health 恒豁免 |
AUTH_EXEMPT_PREFIXES |
空 | 逗号分隔的路径前缀,命中即公开;浏览器 <img>/url() 无法携带认证头,评论封面代理建议 AUTH_EXEMPT_PATHS=/image/fetch |
IMAGE_PROXY_HOSTS |
ehgt.org,s.exhentai.org |
评论封面/预览图代理(/image/fetch)host 白名单,逗号分隔、强制 https;新封面 host 加此免发版 |
LOG_LEVEL |
INFO |
INFO | DEBUG(DEBUG 输出每次出站请求,用于排障) |
完整路由与客户端接入细节见 AGENTS.md。
- 本服务是服务器(多客户端、单 IP 集中请求),比个人客户端更易触发 E-Hentai 封禁。请保持节流参数默认值、善用缓存。
- 图片限额触发返回 429;IP 被封 / 超限触发全局熔断并返回 503,冷却后自动恢复。
- 启用 Basic Auth 时必须位于 HTTPS 反代之后:Basic 凭据仅 base64 编码(非加密),明文传输即泄露。
- 仅限个人使用,请遵守 E-Hentai 服务条款。
本项目采用 Apache License 2.0(见 LICENSE)。
E-Hentai 抓取与解析实现(app/eh/)参考了 JHenTai(Apache License 2.0,Copyright JHenTai contributors):HTML 选择器、页面 URL 约定、会话/cookie 处理与上游异常检测机制均以 JHenTai 为参照,代码为本项目的 Python 独立重写。依据 Apache 2.0 §4,本仓库保留其归属声明(见 THIRD_PARTY_LICENSES)。
中文标签翻译数据来自 EhTagTranslation/Database 项目(CC BY-NC-SA 3.0 中国大陆 许可证,© EhTagTranslation 贡献者):启用 TAG_TRANSLATION_ENABLED 后,OPDS 目录中的标签译名与搜索反查均基于该社区词典。词典仅运行时拉取使用,不随本仓库分发。该数据为非商业性许可,本项目亦仅限个人非商业使用;详见 THIRD_PARTY_LICENSES。