Moth 书就在文件夹里,我只想打开它

#software

最近我新完成并开源了一个新的自托管阅读器:Moth

项目地址:https://github.com/pzweuj/moth

它支持 EPUB、TXT、CBZ 和经典 MOBI,可以保存阅读进度,手机端可以直接作为 PWA 安装使用。

如果只看这段介绍,Moth 似乎并没有什么特别的。

毕竟现在已经有很多成熟的自托管阅读方案,例如 Kavita、Komga、TaleBook。它们拥有更丰富的功能、更完善的元数据管理、更复杂的书库系统,有些还支持 OPDS、多用户、在线书源、自动刮削、Kobo、Kindle 等能力。

所以问题来了:

为什么还要再做一个阅读器?

答案恰恰是:

因为它们做得太多了。


我只是想打开一本书

我最开始的需求其实非常简单。

我的NAS 上已经有整理好的书了:

books/
├── 小说/
│   ├── 三体/
│   │   ├── 01.epub
│   │   ├── 02.epub
│   │   └── 03.epub
│   └── 基地/
│       ├── 01.txt
│       └── 02.txt
└── 漫画/
    └── 漩涡/
        └── 01.cbz

我已经知道这些是什么书。

文件名是我自己起的。

目录也是我自己整理的。

我不需要软件告诉我:

  • 作者是谁;
  • 出版社是什么;
  • 豆瓣评分多少;
  • 应该配哪张封面;
  • 这本书属于什么标签;
  • 它应该被归到哪个 Collection;
  • 这一卷到底是不是某个 Series 的 Volume 3。

我甚至不希望它移动、复制或者修改这些文件。

我只需要:

打开浏览器,找到这本书,然后继续昨天没看完的地方。

这最终成为了 Moth 最核心的一句话:

书放在文件夹里,Moth 负责打开它。


Moth 不是 Library Server

这是 Moth 最核心的设计。

我把 Moth 定义为:

Filesystem Reader,而不是 Library Server。

传统的自托管阅读软件通常会在你的文件系统之上重新建立一个“图书馆”。

大致是:

文件
扫描
识别
Metadata
Series / Author / Volume / Tag
Library
阅读

而 Moth 的流程更接近:

文件系统
索引
打开
阅读
保存进度

Moth 不试图重新解释你的书库。

你的文件系统,本身就是书库。


Kavita、Komga、TaleBook 和 Moth 到底有什么区别?

这几款软件其实并不存在简单的“谁比谁好”。

它们解决的是不同的问题。

我更愿意把它们理解成四种不同的产品哲学。

项目 核心定位 它想解决的问题
Komga 漫画/图书媒体服务器 如何管理好我的漫画收藏
Kavita 数字图书馆 如何建立一个功能完整的私人阅读平台
TaleBook 电子书管理系统 如何获取、整理、管理电子书
Moth 文件阅读器 我已经有书了,怎么简单地读它

差别看似只是功能多少,实际上是:

软件到底应该替用户管理多少东西。


Komga:我想管理我的漫画收藏

Komga 很像漫画世界里的 Plex。

它非常适合拥有大量 CBZ、CBR、EPUB 或漫画资源的用户。

对于这样的用户:

500 GB 漫画
几百个 Series
几千本单行本

Series、Volume、阅读顺序、左右翻页、双页、Webtoon、OPDS、KOReader、Kobo 等能力都有实际意义。

他们的需求是:

我的收藏本来就很复杂,所以我需要一个强大的媒体服务器来管理它。

这种情况下,Komga 的复杂度是有价值的。


Kavita:我想建立自己的数字图书馆

Kavita 更进一步。

它并不仅仅是一个 Reader,而是一个完整的 Library System。

你可以拥有:

用户 A
├── 阅读进度
├── 权限
└── 阅读偏好

用户 B
├── 阅读进度
├── 权限
└── 阅读偏好

再加上:

  • Collections
  • Reading Lists
  • Tags
  • Filters
  • Metadata
  • 多用户
  • 权限控制
  • OPDS
  • 各种第三方客户端

如果你的目标是:

在家里的 NAS 上建立一个属于全家的私人 Kindle / 漫画站。

Kavita 非常合理。

它解决的是“数字图书馆”问题。


TaleBook:我想管理自己的电子书

TaleBook 又是另外一种思路。

它和 Calibre 的关系非常深。

在这个体系里,一本书不只是一个:

xxx.epub

而是:

书名
作者
出版社
封面
简介
ISBN
格式
标签
……

TaleBook 还可以进一步完成:

下载
导入
获取 Metadata
整理
格式转换
推送
阅读

尤其对于中文电子书用户,它提供了很多很实用的能力。

这非常适合一种我很熟悉的用户:

电子书收藏党。

他们不只喜欢读书,也喜欢:

收书、整理书、补封面、修作者、转格式、分类、维护书库。

对于这种用户来说,这些都不是负担,而是乐趣。


然后是 Moth

Moth 面对的是另外一种人。

他的书早就整理好了:

/books
├── 小说
│   ├── 金庸
│   ├── 刘慈欣
│   └── 阿西莫夫
└── 漫画
    ├── 伊藤润二
    └── 龙珠

他看到 Metadata Scraper 的第一反应不是:

太好了!

而可能是:

我为什么需要这个?

他不需要第二套分类。

不需要第二套 Metadata。

不需要第二套文件管理体系。

因为在他看来:

目录就是分类。

这就是 Moth。


文件系统就是配置

Moth 有一个贯穿整个项目的原则:

Filesystem First

例如:

books/
├── 小说/
│   └── 三体/
└── 漫画/
    └── 龙珠/

一级目录就是书架。

二级目录就是系列。

文件就是书。

Moth 不需要你进入后台:

创建书架
→ 填写名称
→ 选择类型
→ 保存
→ 导入
→ 设置 Series

你在文件系统里建立目录,它就已经存在了。

甚至 Moth 的“更多书架”也是类似思路。

如果某个书架不希望长期显示在主页,只需要在对应目录放一个:

hide

文件。

不需要后台设置。

不需要新的数据库表。

不需要“书架显示状态管理系统”。

文件系统本身就是配置文件。


不刮削,是功能

这是 Moth 一个很容易被误解的地方。

Moth:

  • 不抓豆瓣;
  • 不抓作者;
  • 不抓出版社;
  • 不获取在线封面;
  • 不自动整理;
  • 不移动文件;
  • 不修改文件名;
  • 不建立复杂标签体系。

听起来似乎都是:

“缺少的功能。”

但对 Moth 来说不是。

这是设计目标。

因为每增加一层自动化,就会增加一层状态。

比如 Metadata Scraper 看起来只是一个很普通的功能。

但是一旦开始做,就会自然产生:

Metadata
匹配
匹配错了怎么办?
手动编辑
封面管理
作者管理
标签
冲突解决
重新刮削
Metadata 数据库

一个 Reader 很快就开始变成 Library Manager。

而 Moth 不想成为 Library Manager。


不做离线模式,也是设计选择

PWA 很容易让人联想到另一件事情:

能不能把书下载到手机里,断网继续阅读?

Moth 没有做。

这并不是因为 PWA 无法实现离线缓存,而是因为它和 Moth 的使用模型并不一致。

Moth 的内容来自你的 NAS。

当你完全无法访问 NAS 时,本质上你也已经无法访问 NAS 上的文件。

Moth 只是这个文件系统的一个阅读入口,它没有试图再在手机上维护一份独立的离线书库。

也就是说,Moth 的逻辑始终保持一致:

NAS 在线
文件可访问
Moth 可阅读

如果需要在外网使用,可以通过 VPN、Tailscale、Cloudflare Tunnel、反向代理等方式访问自己的 NAS。

如果真正需要长期离线阅读,那么最直接的方式仍然是:

把那个 EPUB、MOBI、TXT 或 CBZ 文件复制到离线设备上。

Moth 不想在服务器书库之外,再创造一个“PWA 本地书库”,然后开始处理:

  • 哪些书已经下载;
  • 哪个版本更新了;
  • 缓存什么时候失效;
  • 阅读进度如何双向合并;
  • 本地存储不够怎么办;
  • 浏览器清理缓存之后怎么办。

这些当然都可以做。

但它们解决的是另一个问题。

Moth 选择不解决。


单用户,也是功能

Moth 从设计上就是:

Single User

它没有复杂的:

User
Role
Permission
Library ACL
Parental Control
Sharing

因为我对电子书有一个很简单的理解:

书籍可以分享,但阅读路径是个人的。

如果我要把《三体》分享给另外一个人,我真正应该分享的是:

三体.epub

而不是:

“给你一个我的 Moth 账号,你登录以后从我的书架里看。”

文件是可以复制、发送和拥有的。

但下面这些东西:

  • 我读到哪一页;
  • 哪本书正在读;
  • 哪些书已经读完;
  • 我喜欢什么字号;
  • 我使用什么主题;
  • 我习惯单页还是双页;

本质上都是个人状态。

所以 Moth 不试图成为一个“大家登录同一个阅读网站”的平台。

每个人完全可以:

自己的文件
+
自己的 Moth
+
自己的阅读进度

如果真的要分享一本书:

分享文件,而不是分享阅读路径。

这也是为什么 Moth 的单用户并不是“以后再补”的功能缺口,而是一个有意保留的边界。

如果你的目标是给全家或者一群朋友建立公共数字图书馆,那么 Kavita 显然更适合。


PWA,而不是再开发三个客户端

Moth 同样没有:

  • Android App;
  • iOS App;
  • Windows 客户端;
  • macOS 客户端。

它只有:

Web。

手机浏览器打开之后,可以直接安装成 PWA。

于是:

桌面
浏览器

手机
PWA

使用的是同一套应用。

阅读进度保存在服务器。

所以我可以:

晚上:

电脑
看到 43%

第二天:

手机
继续 43%

对于一个私人、自托管、单用户的软件来说,我认为已经足够。


四种格式,四条简单的阅读链路

Moth 目前只关心四种格式:

EPUB

直接阅读。

使用 EPUB 本身的章节结构,并保存精确阅读位置。


TXT

这是我比较重视的一种格式。

中文网络小说里仍然存在大量 TXT:

第一章
第二章
第三章
……

Moth 会检测文本编码、识别中文章节,并建立章节索引。

TXT 不需要先手工转换成 EPUB。

直接打开即可。


CBZ

CBZ 本质上就是图片压缩包。

Moth 提供:

  • 单页;
  • 双页;
  • 连续滚动;
  • LTR;
  • RTL。

对于我自己的漫画阅读需求已经足够。


MOBI

经典无 DRM MOBI 在第一次打开时转换成 EPUB 缓存。

原始 MOBI:

不会被修改。

之后直接复用转换结果。

也就是说 Moth 把 MOBI 当成:

输入格式

而不是再重新实现一套 MOBI Web Reader。


Moth 很小

极简设计带来的一个很直接的结果就是:

资源占用非常低。

目前参考环境下:

项目 规模
Docker Image < 30 MB
解包后的运行时 < 150 MB
静息内存 约 5 MB
阅读时内存 约 50 MB

实际数字当然会根据平台、书库和阅读内容变化。

但这基本表达了 Moth 的设计目标:

把它丢进 NAS,然后忘掉它。

对于一台已经跑着:

Jellyfin
Immich
Vaultwarden
FreshRSS
Gitea
Home Assistant
……

的 HomeLab 来说,我并不希望一个“打开小说”的服务再占掉大量资源。

Moth 应该只是安静地待在那里。


/books 甚至可以只读挂载

我很喜欢这一点。

Docker 部署时,书库可以直接:

volumes:
  - ./data:/data
  - /你的书库:/books:ro

注意最后的:

:ro

Moth 根本没有必要拥有修改书库的权限。

原始文件属于用户。

Moth 只在自己的 /data 中保存:

  • SQLite;
  • 阅读进度;
  • 索引;
  • 封面缓存;
  • 派生缓存。

于是即使有一天:

rm -rf data/

你的书仍然全部在那里。

因为:

Moth 的数据库不是你的书库。

你的文件系统才是。


哪些人可能会喜欢 Moth?

我觉得至少有三类。

1. NAS 用户

已经有自己的电子书目录,只需要一个浏览器阅读入口。


2. HomeLab 极简主义者

服务器已经跑了很多东西。

看到一个新项目首先关心的是:

占多少 RAM?
镜像多大?
要几个容器?
要不要 Redis?
要不要 PostgreSQL?

Moth 对这类人的答案基本就是:

一个容器,挂两个目录,然后读书。


3. Filesystem Purist

还有一种人可能尤其喜欢 Moth。

他们相信:

文件系统就是最可靠的数据库之一。

他的书库:

小说/
├── 刘慈欣/
├── 金庸/
└── 阿西莫夫/

已经整理得非常漂亮。

他不希望软件重新告诉他:

刘慈欣属于:
科幻 / 中国文学 / 长篇 / 获奖作品……

他只想:

小说
刘慈欣
三体
打开

Moth 非常适合这种人。


哪些人不应该使用 Moth?

这一点同样重要。

如果你需要:

  • 多用户;
  • OPDS;
  • Kobo / KOReader;
  • 自动刮削 Metadata;
  • 自动整理文件;
  • 在线书源;
  • 作者管理;
  • 标签;
  • 推荐;
  • 上传;
  • 离线书库;
  • 强大的漫画收藏管理;

那么:

不要因为 Moth 更轻就选择 Moth。

Komga、Kavita、TaleBook 可能明显更适合你。

Moth 的目标从来不是:

用更少的资源实现 Kavita。

而是:

解决一个更小的问题。


少,不等于简陋

软件很容易走上一条路线:

用户提出需求
添加功能
又出现新的使用场景
再添加功能
配置越来越多
维护成本越来越高

最后我们会得到一个功能非常强大的平台。

这种软件当然有它存在的意义。

但另外一种软件同样应该存在:

它知道什么时候应该停止。

Moth 希望成为后一种。

未来当然还会修 Bug。

会改善兼容性。

会处理更多奇怪的 EPUB、TXT、MOBI 和 CBZ。

会改善不同浏览器和 PWA 的体验。

会继续优化性能。

但是很多 Feature Request 的答案可能仍然会是:

No。

不是因为不会做。

而是因为不应该做。


Moth 和其他软件最大的区别

如果一定要用一句话概括这四个项目:

Komga:我要管理我的漫画收藏。

Kavita:我要建立自己的数字图书馆。

TaleBook:我要获取、整理和管理电子书。

而 Moth 是:

我已经有书了,让我读。

这就是它存在的理由。


最后

我很喜欢现在 Moth 的结构:

目录就是分类
文件就是书
浏览器就是客户端
SQLite 只保存状态
服务器只负责打开

它没有试图解决电子书世界里的所有问题。

也没有准备成为一个“下一代综合数字内容管理平台”。

它只做一件事情:

书放在文件夹里,Moth 负责打开它。

如果这恰好也是你想要的东西,可以试试看。