起因:念头一冒,就收不住
上回在博客里折腾 Qwen2.5-3B 把这台腾讯云 CVM 折腾完,过了几天它又开始"游手好闲"——4核4G/40G 的小机器,CPU 占用常年 5% 以下。我盯着监控盯出了灵感:要不,再让它干点活?
活儿很快想到了:个人书库。需求很朴素——我从初中开始零散买的纸质书,搬家丢了三次,现在家里一本不剩;而我又舍不得一年花两三百块在京东读书上开会员(虽然已经在用,但总惦记能存一本"自己的")。再加上今年开始带娃,绘本、科普、童书需求量陡增,与其每次现搜,不如自己攒一个。
脑子里第一个冒出来的念头是:"让小布自动去爬。" 我第一反应是——不行。盗版资源站上的书 90% 是近五年内的出版物,扫一本就侵权一本;就是公版书里(Project Gutenberg 那类老古董),也只有作者去世满 50 年以上的才是真正"公版"。能合法采集的,其实只有那一小撮老东西。
好,那就老实一点:先收目录、按需下载,手机上看。不要 APP,要原生支持 OPDS 协议的应用——这是开源阅读器的通用钩子,一套服务端能挂 N 个客户端。
选型:先扫一遍战场
提问:京东读书 APP 能接入吗?
答:不能。京东、微信读书这类是封闭生态,只认自家的书城,第三方书库一概不接。
那就只剩开源阅读器这条路了:
- Legado(阅读)——国产之光,UI 漂亮,OPDS 书源丰富;
- Anx Reader——比 Legado 更新更勤,UI 现代,自带 WebDAV;
- KOReader——主攻 Kindle/电子墨水屏,手机端不算强项;
- Librera Pro——老牌俄罗斯老哥,OPDS 配置简单;
- KyBook(iOS)——iPhone 上的选择,但需要收点小钱。
广告:以上全部开源免费,无广告,无内购。京东、微信读书里买过的书无法导入(DRM 锁死),这是行业惯例,没办法。
手机里现有的书(PDF/EPUB)能直接拖进任何一款。挑哪个主要看眼缘。我自己选 Legado——名字好听("拾遗"),社区活跃。
服务端选 Calibre-Web + OPDS:这是开源电子书管理的事实标准,原版 Calibre-Web 长这样,国内有个中文 fork 叫 TaleBook(talebook/talebook:latest),后台汉化、对中文书名/作者排序做了适配、首字符兼容,对中文用户更友好。Docker 部署几分钟搞定。
路线图敲定:TaleBook 服务端 + OPDS 公开 + Legado/Anx 客户端订阅,开干。
部署:TaleBook 起手式
服务器 IP 还是那位老朋友 106.53.178.213,Caddy 已经把 80/443 站得稳稳的;书库另开一个端口跑 TaleBook:
docker run -d --name talebook --restart unless-stopped \
-p 8080:8080 \
-v /opt/blog/talebook:/data \
-e HALO_ADMIN_USERNAME=admin \
-e TALebook_USER_CREATE_ADMIN=1 \
talebook/talebook:latest
HALO_* 那是 Halo 博客的环境变量;TaleBook 镜像里我用了一个老的环境变量 TALEBOOK_USER_CREATE_ADMIN=1(新版可能改名了,按自己镜像版的 README 来)。容器起来 5 秒,浏览器开 http://106.53.178.213:8080,初始化管理员,登录,注册个普通账号留着给手机用。
然后采集公版书。Project Gutenberg 中文区用 gutendex.com 这个社区维护的镜像,API 干净。注意一个坑:必须带 User-Agent 头,不然返回 403。
import requests, time
UA = {"User-Agent": "tale-book-fetcher/1.0 (mailto:21899412@qq.com)"}
url = "https://gutendex.com/books?languages=zh&page=1"
# 翻页拉元数据,下载 epub
最后用 TaleBook 自带的 calibredb 把书塞进库:
docker exec talebook calibredb add --duplicates --library-path /data/books/library /tmp/zh_001.epub
443 本中文公版书,灌进 /data/books/library/。Web 端书架上终于不是 10 本书了,网页显示 455 本(中间手动加了一本《随身带着星际争霸》凑数)。
移动端:第一关就栽了
书上架了,下一步装客户端。
Legado:打开 GitHub releases,404——这货作者早在三年前就停止更新了,最后一版还停留在 release 页面找不到的尴尬境地。再换一个。
Anx Reader(v1.14.0):通过 ghfast.top 镜像拿到 APK,安装没毛病。配 WebDAV——故事从这里开始。
坑一:Anx 填完 WebDAV 地址提示「链接失败」。
排查:TaleBook 默认没开 WebDAV,需要在容器内的
/data/books/settings/auto.py里把WEBDAV_SYNC_FOLDER打开(同步目录名设个anx),重启。还要chown talebook:talebook /data/anx,不然权限 403。坑二:链接上了,但目录一片空。
排查:直接
curl -X PROPFIND http://106.53.178.213:8080/books/anx测,返回 HTTP 500。翻 TaleBook 源码:内嵌的wsgidav升级后,多了个fs_opts属性,新版代码没初始化。给/var/www/talebook/webserver/webdav/dav_provider.py里MyBooksDavProvider.__init__加一行self.fs_opts = {},重启,207 正常。坑三:链接通了,目录也有了,但书架还是空的——只有我从手机导进去的《随身带着星际争霸》。
排查:我把 443 本书复制到了
/opt/books/data/anx/1/,但 Anx 只认/anx/data/file/这一层。移动到正确路径,443 本书全部出现。
到这里我都觉得自己要赢了。然后,第二天打开 Anx,书架又空了——只剩 178 本。
ls /anx/data/file/ | wc -l
# 178
真相大白:Anx 的 WebDAV 是设备同步协议,不是书库协议。每次连接它会反向同步:服务器上有但本机没的,本机去下;本机有但服务器没注册的,本机去删。我没在 TaleBook 里注册过那些 epub,所以 265 本被判定为"孤儿",删除。
当晚把 443 本书全部导入 TaleBook(calibredb add 注册到元数据库),问题终于稳定下来。但 Anx 这个机制太坑了——新书还没来得及在 TaleBook 后台点"登记",手机一连就给你删掉。
临时结论:Anx 不是 WebDAV 书库客户端,是 WebDAV 同步客户端。不适合当主力阅读器。
补救方案:转 Librera + OPDS——Librera 通过 OPDS 协议从 TaleBook 拉目录(不走 WebDAV),下载到本地只读,绝不删服务器上的东西。验证:OPDS 地址
http://106.53.178.213:8080/opds/,Basic 认证用注册的普通账号密码,50 本/页分页加载,书目录正常,下书正常,进度不丢。Librera 拿来当 OPDS 备份方案。Anx 不删了,但留着作为网页 WebDAV 直连工具——需要从服务器取某个文件时,用 Anx 登上去点对点下载,临时用。
恍然大悟:官方早就有客户端
Librera 用了一周,舒服是舒服,就是两个不爽:
1. 进度不同步:手机上看一半的进度,电脑打开要重新翻页;
2. 没有推送:书库更新了,得手动进 OPDS 刷新。
某天深夜我盯着 TaleBook 项目页发呆,忽然意识到——TaleBook 项目官方客户端,不就是 Moke 吗(中文叫"墨客")。
我去翻了仓库:talebook/moke,Windows / macOS / Linux / Android / iOS 全平台都有 APK 直链。Android v1.1.3 才发布几天,GitHub 上个月还在持续迭代。完全对接 TaleBook 后端——登录、搜索、下载、离线、进度同步、划线笔记,一条龙。
当晚就去拉 APK。
坑四:APK 装不上,提示「解析错误」。
排查:
unzip -t Moke.apk,报End-of-central-directory signature not found——文件被截断了。回头看ls -la文件大小只有 35MB,而真实文件是 198MB。ghfast.top这个 GitHub 镜像代理对大文件偶尔会中途断流,下载没报错,但只传了一半。修:换其他镜像,重下,
unzip -t校验通过(文件末尾D30B 0000——ZIP 结束标志),198MB 正常大小,arm64+armeabi 双架构都齐。教训:从镜像下 APK,下载完务必
unzip -t校验,长度对的不一定是完整的。
APK 重下,安装成功。Moke 登录服务器(http://106.53.178.213:8080,账号密码和网页版一致),455 本书全在,搜索秒出,在线/离线阅读一气呵成,进度、划线全部回写到 TaleBook 数据库。
这才是终局方案。
顺手提的功能建议
Moke 用起来唯一的不爽:进入阅读自动隐藏状态栏,沉浸式全屏。荣耀手机挖孔屏本来就小,再把状态栏藏了,看时间还得从顶部往下滑,挺别扭。手机系统设置(设置→显示和亮度→更多显示设置→应用全屏显示)也管不了 Moke——它走 edge-to-edge 的路子,系统级开关对它无效。
我去翻了 Moke 源码:从 v1.0.0 到 v1.1.3 安卓端的 MainActivity.kt 都是同一个写法——enableEdgeToEdge() 写死,没有全屏开关。但是上游 Readest 阅读器(Moke 嵌的就是它)其实有这个能力——系统设置里有个 alwaysShowStatusBar(默认 false),控制 systemUIVisible 是否常显。入口在 Readest 书库设置菜单,叫 "Always Show Status Bar",仅 isMobileApp 时显示**。
只是 Moke 的 UI 层没有把这个入口暴露出来。
我没有 GitHub 账号(网站打不开),于是从 Moke 项目公开的提交记录里翻到了主要开发者的邮箱 shangzhen0831@163.com,通过代理邮箱把这条建议连同源码定位一起发了过去。看作者回复速度吧,开源作者看邮件频率不定,没回也正常。
收官与版本号
最终阵容:
| 角色 | 软件 | 作用 |
|---|---|---|
| 服务端 | TaleBook(talebook/talebook:latest) |
书目管理、OPDS、用户、Web 阅读 |
| 公版书源 | Project Gutenberg + gutendex API | 443 本中文公版 + 手动 12 本 |
| 安卓主力 | Moke v1.1.3(官方客户端) | 登录即同步、离线、进度回写 |
| 安卓备份 | Librera Pro(OPDS) | 应急,能浏览目录但不同步进度 |
| 安卓辅具 | Anx Reader(WebDAV) | 只用「网页→WebDAV 直连」功能取文件 |
| APK 分发 | Caddy /apk/* |
服务器直链,国内下载免代理 |
占用:TaleBook 容器约 250M 内存,书库本身 1.2G(epub 体积小),磁盘总用量比预想的少——40G 盘至今只用 22G。
服务清单:
- http://106.53.178.213 → "进哥小院"博客(Halo)
- http://106.53.178.213:8080 → 个人书库(TaleBook)
- http://106.53.178.213/apk/Moke-1.1.3-android.apk → Moke 安卓 APK
- http://106.53.213/apk/Librera-9.5.7-arm64.apk → Librera 备份
- http://106.53.213/apk/AnxReader-1.14.0.apk → Anx 备用
进哥小院现在的模样:博客里聊技术、书库里装古今。4核4G 的小机器,又多了一个正经活儿。