Skip to content

Repository files navigation

✦ 微光阅 Lumos Reader

把书留在 NAS,把阅读带在身边。
这是一个给自己家书库用的开源阅读系统:服务端安静地守着文件,Web 和 Android 只在你翻到哪一页时取哪一点内容。

Build image Build Android Changelog

微光阅书库预览

这东西是干什么的?

如果你的书都躺在 NAS 里,而你又不想为了“打开一本书”先把整本文件搬到手机上,那么 Lumos Reader 就是那盏小灯。

  • Go 服务端扫描只读书库,保存书籍元数据、阅读进度、收藏、标签与阅读统计。
  • Web 客户端负责日常浏览和阅读,支持响应式布局、网格/列表视图、漫画系列、多端同步和字体库。
  • Android 客户端通过同一个服务端地址连接,使用 Rust + UniFFI 处理网络与远程文件,并保留轻量 native 阅读路径。
  • 正文、封面和字体都按需读取;可缓存的资源带有明确缓存策略,内容文件支持 HTTP Range。

一句话架构:

NAS 书库(只读)
        │
        ▼
Go 服务端 ── SQLite:进度 / 收藏 / 标签 / 统计
        │
        ├── Web:React + Vite + foliate-js / PDF.js
        └── Android:Compose + Rust/UniFFI + native 阅读组件

能力清单

服务端

  • 扫描 EPUB、MOBI、AZW3、PDF、CBZ/ZIP、TXT。
  • 读取 EPUB 标题、作者、系列、封面、固定版式和翻页方向。
  • 书架可按目录自动生成,也可以显式指定为“图书 / 漫画 / 自动识别”。
  • SQLite 持久化阅读位置、locator、最近阅读时间、累计阅读时长、收藏和标签。
  • 正文支持 Range、ETag、Last-Modified 和私有缓存;封面、漫画页、字体使用独立缓存策略。
  • 漫画提供页目录和单页接口,不需要先把整本 CBZ 解压到磁盘。
  • 内置密码登录、HttpOnly 会话 Cookie、CSP 和路径边界检查。
  • 服务端字体上传、列举和下载;阅读统计包含近 30 日与常读书籍。
  • Web 静态资源编译后嵌入 Go 二进制,最终镜像是无 shell 的 scratch 镜像。

Web 端

  • 最近阅读、图书/漫画分栏、书架/分类树、搜索、格式筛选、标签筛选和只看收藏。
  • 网格与列表两种视图;多卷漫画在网格中合并为一个系列卡片,列表中保留单卷。
  • 书籍详情可编辑收藏和标签;服务端返回的规范化结果是唯一数据来源。
  • EPUB、MOBI、AZW3 使用 foliate-js;固定版式 EPUB 仍然走 EPUB 渲染器。
  • PDF.js 使用 64 KiB Range,关闭整本自动预取;CBZ 只加载当前页附近的小窗口。
  • 章节与卷册在同一个面板中以横向两个 tab 切换,面板内部上下滚动。
  • Web 阅读器退出后刷新书库,阅读进度和阅读时长可被 Android 继续使用。
  • 首页空闲时预热最近阅读书籍的索引,但可取消,不阻塞首屏。
  • 字体可下载到 IndexedDB,阅读时可切换书籍字体、系统字体或自定义字体。

Android 端

  • 输入服务端根地址即可连接 Web 使用的同一书库,阅读进度、收藏、标签和阅读时长共用后端数据。
  • Compose 书库:不同类型/分类左右分页,同一分类内部上下滚动;最近阅读不再被首屏卡片数量截断。
  • 分类显示为“图书/爱情”“漫画/爱情”这类明确层级,多卷漫画仍按系列聚合。
  • 卡片星标、系列整组收藏、书籍详情、标签编辑和收藏/标签筛选。
  • Rust + UniFFI API 客户端负责认证、Cookie、Range、元数据与进度队列。
  • 目录/页面定位保存 locator;PDF、漫画、文本与 EPUB 恢复到更准确的位置。
  • Android 字体库、E-INK 黑白模式、阅读主题、字号和翻页动画。
  • 支持 arm64-v8a 与 armeabi-v7a,本地构建默认低并发以免把 CPU 烤成烤箱。

图书和漫画是怎么识别的?

判定不是只看文件后缀。服务端按以下优先级计算 shelf_kind:

  1. 书架设置显式指定 comic 或 book 时,显式设置优先。
  2. CBZ / ZIP 默认视为漫画。
  3. EPUB 的 rendition:layout=pre-paginated 固定版式默认视为漫画类内容,但仍使用 EPUB 阅读器。
  4. 路径中包含 漫画、manga、comic 或 コミック 时视为漫画。
  5. 其余内容归为图书。

shelf_kind 和 is_comic 是分类信息;真正选择渲染器时,客户端依据格式和 fixed_layout,不会因为一个分类字段就把普通 EPUB 错送到图片阅读器。

推荐目录:

/library/
├── 漫画/
│   └── 爱情/
│       └── 某个系列/
│           ├── 01.cbz
│           └── 02.cbz
└── 小说/
    └── 爱情/
        └── 某本小说.epub

如果目录结构不适合自动识别,进入 Web 或 Android 的书架设置,给书架指定目录和内容类型即可。

Docker Compose:NAS 推荐部署

先准备目录和密码:

cp .env.example .env
# 编辑 .env:至少修改 ADMIN_PASSWORD、COMICS_DIR、BOOKS_DIR
docker compose pull
docker compose up -d

默认只绑定本机 127.0.0.1:8080。想在 NAS 的 7766 端口访问,可以在 .env 设置:

ADMIN_PASSWORD=换成一条真正的强密码
COMICS_DIR=/volume5/漫画
BOOKS_DIR=/volume4/小说
STATE_DIR=./data
FONT_DIR_HOST=./fonts
PORT=7766
SCAN_INTERVAL=15m
PUID=1000
PGID=1000

然后访问 http://NAS地址:7766。升级镜像:

docker compose pull
docker compose up -d --force-recreate

Compose 的书籍卷默认只读,状态库和字体目录可写;不要把 .env、书籍内容或数据库提交到 Git。公网使用时请在反向代理上提供 HTTPS,Android 连接也推荐使用 HTTPS 根地址。

本地开发

需要:Go 1.26、Node.js 26、JDK 17 或更高版本。先构建 Web,再运行 Go 服务端:

cd web
npm ci
npm run build
cd ..

go test ./...
go vet ./...
go run ./cmd/lumosreader

默认服务地址是 http://127.0.0.1:8080,默认书库是 ./library,状态目录是 ./data。本地临时测试可以这样启动:

$env:ADDR = ':7766'
$env:LIBRARY_DIR = 'D:\LumosReader-test\library'
$env:DATA_DIR = 'D:\LumosReader-test\data'
$env:FONTS_DIR = 'D:\LumosReader-test\fonts'
$env:ADMIN_PASSWORD = 'local-only-password'
go run ./cmd/lumosreader

把 EPUB、CBZ、PDF 和字体放进实验目录,打开 http://127.0.0.1:7766 即可;服务端会按 SCAN_INTERVAL 自动扫描,也可以在界面手动重新扫描。

Android 构建

Android 工程位于 clients/android,当前配置为:

项目 版本
Android Gradle Plugin 9.3.1
Gradle 9.6.1
Kotlin / built-in Kotlin 2.4.10 toolchain compatible
Compose BOM 2026.06.01
Activity Compose 1.13.0
JNA 5.19.1
compile / target SDK 36
min SDK 23

Windows 低 CPU 构建示例:

$env:JAVA_HOME = 'C:\Users\Administrator\.jdks\jbr-21.0.11'
$env:CARGO_BUILD_JOBS = '1'
cd clients/android
.\gradlew.bat --no-daemon --max-workers=1 test lint assembleDebug

产物在 app/build/outputs/apk/debug/。native 库由 Rust 构建任务生成,构建目录和 .so 不需要手动加入 Git。

API 速览

客户端只需要保存服务端根地址,例如 https://nas.example.com:7767,不要填写 Web 页面路径。

方法 路径 用途
GET /api/server 服务发现、API 版本和支持格式
GET/POST/DELETE /api/session 查询、登录、退出会话
GET /api/books /api/books/{id} 书库与书籍详情
GET /api/books/{id}/content EPUB/MOBI/AZW3/PDF/TXT 正文,支持 Range
GET /api/books/{id}/cover 书籍封面
GET /api/books/{id}/pages CBZ/ZIP 页目录
GET /api/books/{id}/pages/{page} CBZ/ZIP 单页图片
GET/PUT /api/books/{id}/progress 阅读位置与 locator
PUT /api/books/{id}/metadata 收藏和标签
POST /api/books/{id}/reading-time 累计阅读时长
GET/POST /api/fonts 字体列表和上传
GET /api/fonts/{name} 下载字体
GET /api/stats 阅读统计
GET/POST /api/scan 查询或触发扫描
GET/PUT /api/shelves 书架设置

API 当前版本为 v4。新字段按可选字段兼容处理;内容请求可能返回 206 Partial Content,客户端必须正确处理 Content-Range。

发布流程

推送 main 后,服务端/Web 镜像 workflow 会构建并推送:

ghcr.io/alumos/lumosreader:latest
ghcr.io/alumos/lumosreader:sha-<commit>
ghcr.io/alumos/lumosreader:<semver>

Android 使用统一的 vX.Y.Z tag 构建两个 ABI 的签名 APK 并创建 GitHub Release;同一个 tag 也会为镜像生成 semver 标签。正式 Android Release 需要仓库配置签名相关 Secrets。

本仓库的版本记录见 CHANGELOG.md

目录速览

cmd/lumosreader/       Go 程序入口
internal/server/       HTTP API、扫描器、EPUB 元数据、SQLite、运行时
web/                   React/Vite 前端与嵌入资源
clients/android/       Compose 应用、Rust/UniFFI、native 阅读组件
scripts/               UI 与回归检查脚本
docs/                  预览图和开发设计记录
.github/workflows/     镜像与 Android 自动构建

最后说两句安全的

这个项目不包含任何书籍内容。部署者需要自行确认挂载文件的版权和访问权限。密码不要写进仓库,公网请使用 HTTPS,书库尽量保持只读挂载,状态目录和字体目录单独备份。

如果遇到问题,带上服务端版本、客户端版本、书籍格式和日志里的错误信息开 Issue;不要上传整本书。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages