feat(jm): 新增 JM 漫画下载打包服务(9004,移植自 qqbot jm 模块)

- source/jm/jm.py:aiohttp + jmcomic,GET /jm/get_comic?id=xxx[&range=起-止]
  下载至 cache/jm 隐藏暂存目录 → webp 转 jpg 打包 ZIP → HTTP 直返 → 600s 延时删除
  单次上限 30 章;per-album 锁防并发;cache_sweep 兜底清理;/jm/cache_info 运维接口
- source/main.py:聚合层注册 /jm/get_comic(新增 JM_BACKEND,复用 proxy())
- systemd/jm.service:独立系统服务(Restart=always,日志 /var/log/jm.log)
- README / 部署与运维文档同步更新
This commit is contained in:
RRRosmontis
2026-09-23 10:58:48 +08:00
parent 9ba194ae97
commit 9ecbbbc6d8
5 changed files with 367 additions and 15 deletions
+27 -9
View File
@@ -2,7 +2,7 @@
统一接口服务。线上入口:`https://openapi.rrrosmontis.icu`
聚合层把 **pixiv 图片服务**、**bilibili 视频服务**与 **dufe 银杏签到码查询服务**收敛到单一对外入口。四个后端进程全部只监听 `127.0.0.1`,对外仅由 nginx 暴露 80/443——即"一个入口、零暴露端口"。
聚合层把 **pixiv 图片服务**、**bilibili 视频服务**、**dufe 银杏签到码查询服务**与 **jm 漫画下载服务**收敛到单一对外入口。五个后端进程全部只监听 `127.0.0.1`,对外仅由 nginx 暴露 80/443——即"一个入口、零暴露端口"。
## 架构
@@ -11,6 +11,7 @@
├── /pixiv/get_image?id=xxx -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9007 pixiv
├── /bilibili/get_video?bv=xxx -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9006 bilibili
├── /dufe/ginkgostu_sign_in -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9005 dufe 签到码查询(GET|POST)
├── /jm/get_comic?id=xxx -> 127.0.0.1:9008 main.py 聚合层 -> 127.0.0.1:9004 jm
├── /img-master/ /img-original/ -> nginx 直接反代 i.pximg.net(无本地后端)
└── / -> 127.0.0.1:9008(302 跳转文章页)
```
@@ -23,6 +24,7 @@
| pixiv | `127.0.0.1:9007` | `source/pixiv/pixiv.py`(Flask + waitress) | 图片代理 |
| bilibili | `127.0.0.1:9006` | `source/bilibili/bilibili.py`(aiohttp + yt-dlp) | 视频代理 |
| dufe 签到 | `127.0.0.1:9005` | `source/dufe/ginkgostu_sign_in/ginkgo_sign_in.py`(Flask + waitress 16 线程) | 银杏(DUFE)签到码查询,`GET\|POST /dufe/ginkgostu_sign_in` |
| jm | `127.0.0.1:9004` | `source/jm/jm.py`(aiohttp + jmcomic) | 漫画下载打包(移植自 qqbot jm 模块),`GET /jm/get_comic?id=xxx[&range=起-止]` |
| pixiv-token-refresh | — | `source/pixiv/refresh_token.py` | token 刷新守护(每 30–60 分钟随机) |
| bilibili-refresh | — | `source/bilibili/refresh_cookie.py` | cookie 刷新守护(每 50–70 分钟随机) |
@@ -33,7 +35,7 @@
```
source/
├── main.py # 聚合层 (127.0.0.1:9008),四条路由:/ /pixiv/get_image /bilibili/get_video /dufe/ginkgostu_sign_in
├── main.py # 聚合层 (127.0.0.1:9008),五条路由:/ /pixiv/get_image /bilibili/get_video /dufe/ginkgostu_sign_in /jm/get_comic
├── pixiv/
│ ├── pixiv.py # Pixiv 图片服务 (127.0.0.1:9007)
│ ├── refresh_token.py # Pixiv OAuth token 刷新守护
@@ -46,18 +48,22 @@ source/
├── dufe/
│ └── ginkgostu_sign_in/
│ └── ginkgo_sign_in.py # 银杏(DUFE)签到码查询 (127.0.0.1:9005, GET|POST)
├── jm/
│ └── jm.py # JM 漫画下载打包服务 (127.0.0.1:9004)
└── cache/ # 运行时生成,不入库
├── pixiv/images # 图片缓存
└── bilibili/ # 视频缓存
├── bilibili/ # 视频缓存
└── jm/ # jm 临时 ZIP(TTL 10 分钟,返回后删除)
nginx/
└── openapi.rrrosmontis.icu.conf # 统一入口(/pixiv/ /bilibili/ /img-master/ /img-original/)
└── openapi.rrrosmontis.icu.conf # 统一入口(/pixiv/ /bilibili/ /img-master/ /img-original/,其余路径由 location / 兜底至聚合层)
systemd/
├── openapi.service # 聚合层
├── pixiv.service # Pixiv 图片服务(waitress --threads=8)
├── bilibili.service # bilibili 视频服务
├── bilibili-refresh.service # cookie 刷新守护
├── pixiv-token-refresh.service # Pixiv token 刷新守护
└── ginkgo_sign_in.service # 银杏(DUFE)签到码查询服务
├── ginkgo_sign_in.service # 银杏(DUFE)签到码查询服务
└── jm.service # JM 漫画下载打包服务
docs/
└── 部署与运维.md # 线上服务器的路径、缓存、迁移步骤与变更记录
```
@@ -90,14 +96,23 @@ curl -s 127.0.0.1:9006/bilibili/cache_info
# {cache_dir, cache_max_bytes, cache_ttl_seconds, count, total_bytes, free_bytes, files[]}
```
**jm(漫画)**:**非持久缓存**,与 bilibili 不同——ZIP 仅作临时产物
- 请求 `id`(+ 可选 `range=起-止`,单次 ≤30 章)→ jmcomic 下载到 `cache/jm/` 内隐藏暂存目录 `.dl-<id>-XXXX` → webp 统一转 jpg 打包 ZIP(`{标题}_{id}_ch{起}-{止}.zip`)→ HTTP 返回
- 打包完成即 `rmtree` 图片暂存目录;ZIP 在返回后 `ZIP_TTL_SECONDS`(默认 600 秒)延时删除
- TTL 窗口内重复请求同一 ZIP 直接复用并重新计时;每次请求附带 `_cache_sweep()` 兜底清理过期 ZIP(防进程重启残留)
- 同一本子串行处理(per-album asyncio.Lock),并发请求同本返回 409
- 运维接口:`curl -s 127.0.0.1:9004/jm/cache_info`
## 部署
1. **Python 环境**:各服务共用一个环境,需含 `flask`、`waitress`、`aiohttp`、`httpx`、`yt-dlp`
1. **Python 环境**:各服务共用一个环境,需含 `flask`、`waitress`、`aiohttp`、`httpx`、`yt-dlp`、`jmcomic`、`pillow`
2. **凭据**:
- 复制 `source/pixiv/tokens.template.json` 为 `source/pixiv/tokens.json`(`chmod 600`)并填入实际 pixiv token
- 放置 `source/bilibili/cookies.txt`(`name=value` 格式),或运行 `qr_login.py` 扫码生成
- jm 无需凭据,但要求部署机可直连源站(jmcomic 会自动更新 API 域名)
3. **服务**:将 `systemd/*.service` 安装到 `/etc/systemd/system/`,然后
`systemctl daemon-reload && systemctl enable --now openapi pixiv bilibili ginkgo_sign_in bilibili-refresh pixiv-token-refresh`
`systemctl daemon-reload && systemctl enable --now openapi pixiv bilibili ginkgo_sign_in jm bilibili-refresh pixiv-token-refresh`
4. **nginx**:将 `nginx/openapi.rrrosmontis.icu.conf` 放入 `/etc/nginx/conf.d/`(需存在 `$connection_upgrade` 的 map 定义),`nginx -t && systemctl reload nginx`
5. **软链**(可选):`ln -s <部署根>/source/cache/pixiv/images /home/images`
6. **定时任务**:无 cron/timer——两个刷新守护均为常驻循环(内部随机间隔),systemd `Restart=always` 兜底
@@ -109,18 +124,21 @@ curl -k "https://openapi.rrrosmontis.icu/pixiv/get_image?id=147113746"
curl -k "https://openapi.rrrosmontis.icu/bilibili/get_video?bv=BV16a41157Q4"
curl -kI "https://openapi.rrrosmontis.icu/bilibili/get_video?bv=BV16a41157Q4" # 只看头部,省流量
curl -k "https://openapi.rrrosmontis.icu/dufe/ginkgostu_sign_in" # 无参应返回 400 JSON
curl -k "https://openapi.rrrosmontis.icu/jm/get_comic?id=350234" -o jm.zip # 返回 ZIP
curl -s 127.0.0.1:9006/bilibili/cache_info
curl -s 127.0.0.1:9004/jm/cache_info
```
端口暴露自检(四个后端都应"不可达",仅 443/80 通):
端口暴露自检(五个后端都应"不可达",仅 443/80 通):
```bash
for p in 9005 9006 9007 9008; do timeout 3 bash -c "cat < /dev/null > /dev/tcp/<host>/$p" 2>/dev/null \
for p in 9004 9005 9006 9007 9008; do timeout 3 bash -c "cat < /dev/null > /dev/tcp/<host>/$p" 2>/dev/null \
&& echo "$p 可连通(异常)" || echo "$p 不可达(正常)"; done
```
## 版本要点
- **2026-09-23**:新增 **jm 漫画下载打包服务**(`source/jm/jm.py`,aiohttp + jmcomic,`127.0.0.1:9004`,端点 `GET /jm/get_comic?id=xxx[&range=起-止]`),移植自 qqbot 的 jm 模块(去掉 QQ/代理轮换/上传 cf 逻辑,改为 HTTP 直返 ZIP;hk 可直连源站无需代理)。ZIP 为临时产物:返回后 600 秒延时删除,TTL 窗口内同名请求直接复用。聚合层 `main.py` 注册路由(新增 `JM_BACKEND`,复用既有 `proxy()`),nginx 无需改动(`location /` 兜底)
- **2026-09-13**:新增 **dufe 银杏签到码查询服务**(`source/dufe/ginkgostu_sign_in/ginkgo_sign_in.py`,Flask + waitress 16 线程,`127.0.0.1:9005`,端点 `GET|POST /dufe/ginkgostu_sign_in`),并在聚合层 `main.py` 注册该路由——为此新增 `proxy_forward()`(保留请求方法与请求体,原 `proxy()` 保持固定 GET 不变,零改动既有行为)。同期修复该服务两处缺陷:base64 参数补 `urllib.parse.unquote`(兼容 iOS 快捷指令的 `%3D` 编码),`commit` 判定改为 fail-closed(须 `commit.success is True` 才算签到成功)
- **2026-09-12**
- 端口重排 `9888/9088/19990 → 9008/9007/9006`,全部仅绑 `127.0.0.1`;pixiv 由 `0.0.0.0:9088` 收口