乐于分享
好东西不私藏

用 AI 一晚上撸了个自托管阅读器,兼容 Legado 5000+ 书源

用 AI 一晚上撸了个自托管阅读器,兼容 Legado 5000+ 书源

用 AI 一晚上撸了个自托管阅读器,兼容 Legado 5000+ 书源

NAS 上一直跑着 hectorqin/reader 当阅读器,能用,但越用越不爽:

  • UI 像 2015 年的产物,手机上看书体验很差
  • 没有 PWA,每次都要开浏览器找地址
  • TTS 朗读?不存在的
  • 想加个暗黑模式都得去改 Java 代码

既然手头有 AI 能写代码,不如自己从零搭一个。一晚上干完,从书源引擎到前端阅读器到 CI/CD 自动部署,全链路跑通。


目标:兼容 Legado 书源,但不复刻 Legado

Legado(阅读 App)的生态里有 5000+ 社区维护的书源,格式是 JSON,里面定义了"去哪搜索、怎么提取书名、怎么拿章节列表、怎么取正文"的规则。

现有方案(hectorqin/reader)的思路是用 Java 复刻 Legado 的规则引擎。我换了个策略:

读 Legado 格式,用 Python 自己执行。

不去复刻 Legado 的 java.* bridge 全集,而是用 Python 生态的解析库直接处理:

规则类型
Legado 语法
我的实现
CSS 选择器
@css:div.title
BeautifulSoup4
JSOUP 简写
class.name@tag.a@href
自写适配层
XPath
//div[@class="content"]
lxml
JSONPath
$.data.list[*].title
jsonpath-ng
正则
##作者:(.+)
Python re
JavaScript
<js>result.replace(...)</js>
QuickJS 沙盒

这样做的好处是:70% 以上的书源用简单选择器就能跑通,不需要完整的 JS bridge。剩下那些复杂的(加密、动态分页)后续再加。


技术栈:一个 Docker 容器搞定

后端:Python + FastAPI + SQLite + httpx前端:React + TypeScript + Tailwind + PWA部署:Docker 单容器,NAS 一键跑

为什么选 Python 不用 Java/Kotlin?

  1. 解析库生态好——BS4、lxml、jsonpath-ng 都是成熟工具
  2. 异步 I/O 原生支持——搜索时并发请求几十个书源
  3. 写起来快——一晚上要干完,不想写模板代码

为什么单容器?NAS 上跑 Docker,一个镜像包含前后端所有东西,docker run 一行命令启动,不搞 docker-compose 多服务那套。


核心能力

1. 搜索:流式返回,不等超时

267 个书源并发搜索,不可能等所有都返回再显示——有些站早就挂了,等它超时要 15 秒。

我用 SSE(Server-Sent Events)做流式推送:每个书源一返回结果就立刻推给前端,页面实时刷新。

用户输入关键词  → 后端并发请求 267 个书源(信号量限制 10 并发)  → 书源 A 返回 → 立即推送 → 前端追加显示  → 书源 B 返回 → 立即推送 → 前端追加显示  → ...  → 全部完成或超时 → 发送 [DONE]

体感差异很大:从"等 15 秒看到一堆结果"变成"1 秒就开始出结果,持续刷新"。

2. 规则引擎:处理 Legado 的奇葩语法

Legado 的规则语法在 JSON 里写得很随意,各种换行、嵌套、组合:

// 先取 JSONPath,再正则替换$.groupID##.*_##// URL 模板引用已解析的字段https://api.example.com/chapters?id={{book.kind}}// 按文本内容查找元素text.在线阅读@href

这些都不是标准语法,需要一个一个适配。我的引擎做了:

  • ## 内联正则:$.field##regex##replacement
  • {{book.field}} 模板变量:URL 里引用其他已解析字段
  • text.xxx 选择器:按文本内容查找 DOM 元素
  • 自动 Referer:很多 API 验证来源,自动加上目标域名的 Referer
  • URL 清理:Legado JSON 里的 URL 经常有换行符、相对路径,全部自动修正

3. 前端:响应式 + PWA

一套代码适配手机和桌面:

  • 手机:全屏沉浸阅读,底部导航
  • 桌面:侧边章节栏 + 居中阅读区

PWA 支持意味着可以"添加到主屏幕"当独立 App 用,不需要每次打开浏览器输地址。

阅读设置:字号、行距、主题(亮色/暗色/护眼)都可以调。


CI/CD:push 即部署

整条流水线全自动:

git push main  → GitHub Actions 构建 Docker 镜像  → 推送到 Docker Hub  → SSH 到 NAS(通过 Cloudflare Tunnel)  → docker pull + restart

从提交代码到 NAS 上跑起新版本,大概 30 秒。改一行代码,手机刷新就能看到效果。

# .github/workflows/deploy.yml 核心步骤- name: Build and push  uses: docker/build-push-action@v6  with:    tags: rio22/reader:latest- name: Deploy to NAS  run: |    ssh "$NAS_USER@$NAS_HOST" "docker pull rio22/reader:latest && \      docker stop reader; docker rm reader; \      docker run -d --name reader -p 8880:8080 rio22/reader:latest"

SSH 走的是 Cloudflare Tunnel(没有公网 IP),GitHub Actions 里装了 cloudflared 做代理。


部署:一行命令

docker run -d \  --name reader \  --restart unless-stopped \  -p 8880:8080 \  -v /volume1/docker/reader/data:/app/data \  rio22/reader:latest
参数
说明
-p 8880:8080
外部端口 8880 映射到容器 8080,按需改
-v .../data:/app/data
数据持久化(SQLite + 缓存),路径改成你的
rio22/reader:latest
Docker Hub 镜像,push main 自动构建

启动后访问 http://NAS-IP:8880,在"书源"页面贴入书源 JSON 地址即可导入。推荐用这个(约 270 个源):

https://cdn.jsdelivr.net/gh/tickmao/Novel@master/sources/legado/full.json

实际效果

搜索"十日终焉":

  • 0.5 秒开始出结果
  • 最终 122 条,来自多个书源
  • 点开第一条:1496 章
  • 点击章节:正文 2345 字,排版干净

对比 hectorqin/reader:

hectorqin/reader
我的版本
语言
Java/Kotlin
Python + React
UI
传统服务端渲染
现代 SPA + PWA
搜索体验
等全部加载
SSE 流式实时
手机适配
勉强能用
响应式原生体验
部署
docker-compose
单容器
开发效率
改 Java 重编译
热重载秒级反馈

踩的坑

1. 书源 URL 里有换行符

Legado 的 JSON 文件里,searchUrl 字段经常有 \n,导致 HTTP 请求直接报错。加了一行 .replace("\n", "") 解决。

2. 相对路径 URL

很多书源的 searchUrl 写的是 /search?q=xxx 而不是完整 URL。需要自动拼接书源域名。

3. Referer 验证

不少 API(QQ 阅读等)会验证 Referer 头。之前没加,返回 "incorrect referer"。现在所有请求自动带上目标域名作为 Referer。

4. 缓存了错误响应

早期请求失败的响应被缓存了,后来修了代码但结果不变。加了"小于 50 字不缓存"的规则防止脏缓存。

5. 通过隧道操作隧道本身

CI 部署时 SSH 走 Cloudflare Tunnel。如果操作不当把隧道搞断了,就连不上 NAS 了。(这个坑我之前单独写过一篇)


功能状态

功能
状态
说明
书源导入(URL / 文件)
✅ 已完成
兼容 Legado JSON 格式
多源搜索
✅ 已完成
SSE 流式输出,实时显示
章节列表
✅ 已完成
支持分页目录
正文阅读(滚动模式)
✅ 已完成
字号/行距/主题可调
响应式布局
✅ 已完成
手机/桌面自适应
PWA 安装
✅ 已完成
可添加到主屏幕
Docker 一键部署
✅ 已完成
单容器 + CI/CD 自动构建
翻页模式
🚧 未完成
左右滑动模拟纸书
漫画阅读
🚧 未完成
条漫 + 页漫双模式
整本下载 / 离线缓存
🚧 未完成
IndexedDB 客户端存储
导出 epub / txt
🚧 未完成
后端生成,前端触发下载
TTS 朗读
🚧 未完成
Edge TTS + 句子高亮
书架管理
🚧 未完成
收藏、分类、更新检测
复杂 JS 书源
🚧 未完成
完整 java.* bridge

代码开源在 GitHub:qq148376839/reader


一句话总结

兼容 Legado 书源格式 + Python 自建执行引擎 + React PWA 前端 + Docker 单容器部署 = 一个能用的自托管阅读器,一晚上从零到部署上线。

如果你也在 NAS 上找阅读方案,可以试试。书源直接用 Legado 社区的,不用重新找。