v0.2.0
下载与安装
本页产物按平台与 CPU 架构拆分,选择与你的设备匹配的一个下载即可。
Android(APK,按 ABI 拆分)
- arm64-v8a:近几年的主流手机 / 平板(64 位),绝大多数设备选这个
- armeabi-v7a:仅支持 32 位应用的老设备
- x86_64 / x86:Android 模拟器(分别对应 64 / 32 位镜像)
系统要求 Android 7.0(API 24)及以上。直接覆盖安装即可升级,书架、书源、设置等数据保留在设备本地,不受影响。
Linux(x86_64)
- AppImage:下载后
chmod +x再运行,免安装; - deb:Debian / Ubuntu 系,
sudo apt install ./readerx_*.deb; - rpm:Fedora / openSUSE 系,
sudo dnf install ./readerx-*.rpm。
需要系统已装 WebKitGTK 4.1 与 GTK3(主流桌面发行版通常自带)。
Windows(x86_64 / aarch64)
安装包文件名里的架构要与设备匹配:x64 用于 Intel / AMD 处理器,arm64 用于骁龙 X 等 ARM 笔记本。
WebView2 运行时由 Windows 10/11 自带,无需另外安装。
关于签名
Android APK 已签名;Linux 与 Windows 安装包未做代码签名:
- Windows 安装时可能出现 SmartScreen 提示,选择「仍要运行」即可;
- Linux 下 AppImage 需要自行赋予可执行权限。
使用中遇到问题欢迎到 Issues 反馈,请附上平台、设备型号与系统版本。
以下为本版本更新内容:
Added
-
自定义源解码失败的修复指南:Linux 桌面端听书走 WebKitGTK,音频解码交给 GStreamer,
而多数发行版出于 MP3 专利许可默认不带 MP3 解码器 —— 自定义源返回 mp3 时只剩一句
「音频解码失败」,用户无从下手。现在这种失败会当场弹出修复指南(不必等连续失败 5 句
才报错,每次朗读会话只弹一次,重试或重新起播后重新武装):说明缺的是什么、按发行版
给出 apt / dnf(含 RPM Fusion)/ pacman / zypper 的安装命令(点一下整条复制,
应用不代为执行)、gst-inspect-1.0 | grep -i mp3验证方法与重启应用、查看应用日志、
改用 wav/ogg 等补充说明;解码失败时还会把字节的真实格式(MP3 / WAV / Ogg…)与体积
记进日志,便于区分「系统缺解码器」与「源返回了非音频内容」。 -
统一日志系统:此前诊断信息散落在
println!/console.error里,Android 上用户根本拿不到
—— 出问题只能靠连电脑抓chrome://inspect。现在前后端共用一个出口:- 新增独立 crate
readerx-log(App、书源引擎、独立二进制共用):log门面 + 级别过滤
(READERX_LOG风格规格,支持readerx_source=debug这类按模块覆盖)、
文件落盘与轮转(单文件 2 MB、保留 3 份历史)、标准错误输出、Android logcat 目标
(adb logcat -s readerx,几行 FFI,不引额外依赖)、以及日志脱敏工具; - 日志写在
<应用数据目录>/logs/readerx.log,设置 → 调试 → 应用日志可直接查看:
按「全部 / 信息 / 警告 / 错误」筛选、复制到剪贴板、连点两次清空,并可在「常规 / 详细」
之间切换(详细 =debug,会记住选择,前端最低级别同步放开);手机上没有这个入口
就只能连电脑,因此它对所有平台开放(「开发者工具」仍仅桌面端); - 必要的日志补齐:应用启动(版本 / 平台 / 数据目录 / 日志文件 / 生效级别)、
panic hook(含位置,写日志后再弹提示)、全部命令入口(成功记耗时、失败记真实原因)、
书库扫描与无法解析的书籍文件、图片迁移与清理、听书缓存淘汰与清空、
书源引擎(每次 HTTP 请求的地址 / 状态 / 字节数 / 耗时、Cloudflare 自动认证的每条分支、
入口函数调用与逐章拉取结果、书源文件解析告警、登录态落盘、认证后端步骤)、
以及前端(启动各阶段、导入 / 拉取 / 下载 / 解析 / 听书的成败与耗时、被静默降级的读取失败); - 凭据绝不进日志:Cookie / token / 密码 / 存储快照一律只记条数,URL 去掉
user:pass@
并把token/sign/password等查询参数打码(readerx_log::redact),
日志因此可以放心让用户复制出来贴进 issue; readerx-source独立二进制的诊断输出改走同一套日志(结果输出仍走标准输出),
--verbose等于把级别切到debug,日志文件与 App 同规则。说明见
docs/logging.md。
- 新增独立 crate
-
批量下载正文可指定章节范围:此前下载面板只有「下载剩余全部」,想只补中间一段、或先下前几十章
离线应急都做不到 —— 现在面板里多了一行下载范围:默认「全书」,点「起始章 / 结束章」打开章节
抽屉挑选(可直接输入章节序号,也能在列表里点,尚未缓存的章标「未缓存」),即只下第 x–y 章,
面板里同步显示范围内还有多少章待下载。所选范围同时约束图片阶段(含图章节不会把范围外的图片
也拉下来),下载按钮、进度与结果提示都按范围给出文案;正在读的那一章不受范围限制,照旧优先
取回,读到哪一章就先出哪一章。
Changed
-
逐章下载正文(不再按 20 章打包):此前批量下载把范围内章节按 20 章一批交给引擎,一批要等
最慢的那一章回来才落盘,进度一次跳 20 章,中途停止还会丢掉整批已经下好的正文 —— 现在每一章
都是一个任务:引擎按「书源并发」起若干 worker 逐章领取,取回一章立刻解析、写回书架并推进度,
慢章 / 失败章不拖住别的章节。正在读的那一章会被插到队首(读到哪一章就先取哪一章,
不必排在几百章后面),点「停止下载」后引擎立刻不再领取新章节、已取回的部分照常保留;
正文逐章写盘再按小批合并(正在读的那一章立即写),大书不再把同一份书籍 JSON 反复读写上千遍。 -
打开书签抽屉自动定位到当前章节:书签列表按章节分组后往往很长,此前每次都要从第一条往下翻
—— 现在打开即滚到当前章节那张卡片;本章没有书签时定位到章节序号最近的一章(同样近取靠后的一章,
顺着阅读方向先看还没读到的那条)。卡片放得下就居中、书签太多放不下则顶对齐,保证卡片标题与
开头几条始终可见;只滚动抽屉自身,背后的正文阅读位置不受影响。 -
书籍详情页点书名 / 作者就能搜书、搜作者:详情页顶部的书名与作者改为可点(文字末尾的放大镜
即提示),点一下跳到「发现」页并按该词做一次全源快速搜索 —— 与点击标签同一个入口
(/discover?q=深链,书源就绪后自动开搜),标签的既有行为不变;发现页里点搜索结果弹出的
「在线书详情」抽屉同样可点(就地收起抽屉重搜)。作者为空时仍只显示「佚名」且不可点,
不会拿「佚名」去搜;搜索词取页面上显示的简繁转换副本,看到什么就搜什么。 -
桌面端支持(Linux / Windows):同一份前端现在跑在两种外壳里 —— 手机端仍是底部 Tab 的手机列
(不动手感),窗口宽度 ≥900px 时换成侧边导航 + 内容区的桌面外壳(src/shell/DesktopStage.tsx;
窄窗口自动回到手机外壳,见src/lib/platform.ts的断点)。页面组件、路由、本地书库、保活页面
注册表两端共用一份,没有为桌面复制页面;切换页面在桌面端不做横向滑动动画,直接换内容区。
侧边栏只在三个主 Tab(书架 / 发现 / 设置)显示 —— 阅读页、书源管理、书籍详情等其余页面
整条不显示、内容区铺满(与手机端「次级页不显示底部 Tab」同一套口径);主 Tab 里还能用标题行
右端的按钮把侧边栏收起成图标栏(收起后这一行只剩按钮本身,与三个主 Tab 图标一样居中在
图标栏中线上;再点展开,形态由readerx.sidebarCollapsed记住),
侧边栏的实际占位写进--sidebar-w。
挂在body上的底部抽屉 / 操作条改用--app-column变量(跟随侧边栏的显示与收展),
桌面端不会横跨整个窗口。
桌面端另有三项系统集成:窗口默认 1180×820、最小 420×560 并居中(tauri.conf.json)、
原生文件选择器导入本地书(Rust 侧readerx_pick_book_file读成字节后复用既有的
TXT / EPUB / PDF 解析与「同名书重新导入」交互,手机端不受影响)、Esc 返回上级页面。 -
桌面端设置页可以打开开发者工具:设置页新增「调试 → 开发者工具」(仅桌面端显示,
手机端与浏览器里不出现),点一下打开当前 WebView 的检查器(Rust 侧readerx_open_devtools,
打开的是发起调用的那个 WebView)。release 包同样可用 ——src-tauri/Cargo.toml的devtools
feature(默认开启)转发到tauri/devtools,不启用时命令如实报错而不是让按钮点了没反应
(Android 的 WebView 不提供该 API,真机调试仍走chrome://inspect)。 -
桌面端网页登录窗口:书源「网页登录」不再只限 Android —— Linux / Windows 上开一个独立登录窗口
(plugins/tauri-plugin-webview-login的桌面实现),登录完点窗口里的「完成」或直接关窗即收尾。
抓取口径与 Android 一致:Cookie 走内核 Cookie 库(Linux 直接用 WebKitGTK 原生CookieManager,
含 httpOnly 的cf_clearance/__cf_bm),非 Cookie 登录信息走存储探针(脚本由核心 crate 提供、
插件原样执行)。窗口的 User-Agent 与会话实际发出的 UA 对齐(cf_clearance与 UA + IP 绑定,
两边不一致会导致「验证过了但请求仍被拦」),书源未写 UA 时用内置默认值。
平台差异如实记录在desktop.rs与docs/cloudflare.md:Android / Windows / macOS 的注入脚本跑在
页面主世界,localStorage 快照采得到;Linux 的 WebKitGTK 把宿主脚本放在隔离世界,
该平台只以 Cookie 为登录态来源(cf.auto与webview.storage()的降级分支照常工作,
登录态文件跨端共享,在手机上采集到的快照在 Linux 上照样读得到)。 -
CI 补齐桌面平台并统一构建环境:新增
.github/workflows/build-desktop.yml(手动触发,按 ABI
分别出 Linux x86_64 的 AppImage/deb/rpm 与 Windows x86_64 / aarch64 的 NSIS 包,只传 artifact
不发布);
release.yml改为一条 tag 一次发布三平台(Android 签名 APK + Linux x86_64 包 + Windows
x86_64 / aarch64 安装包汇总到同一个 Release,按 ABI 分开构建、重名时自动补架构后缀,
版本校验抽成独立 job,发布说明如实注明桌面包未签名);Android 两条工作流接上
共享的 composite action(.github/actions/setup-build)并加 Gradle 缓存与并发取消。
质量门槛(类型检查 / 前端构建 / Rust 单测)只在本地按需跑,不新增 Actions 工作流。
Changed
-
发布说明在 CHANGELOG 缺本版条目时回退到 [Unreleased]:
scripts/release-notes.mjs原先找不到
版本区块就直接失败(发版工作流随之中断),现在改为回退到## [Unreleased]区块 —— 忘写本版
条目时 Release 仍有一份真实的变更说明(就是这批「已提交但未发布」的内容),同时在 stderr 打一条
告警,CI 日志里能看见该补的条目。连 Unreleased 也没有、或两边都没有实际内容(只剩空的
### Added之类小标题)时照旧非零退出,不会发出空说明。 -
书架封面改为「固定宽度 + 每行本数自适应」:原先写死一行三本,封面会被容器宽度拉伸
—— 桌面内容区里三本各占几百像素。现在封面固定 96px,用auto-fill按容器宽度决定一行放
几本,轨道整体居中、余量均分到两侧(不再贴边);手机列与桌面内容区共用这一套规则,
封面尺寸不变,只是每行本数随宽度增减(360px 手机仍 3 列、480px 视口 4 列、桌面内容区更多)。 -
听书缓存额度可调,不再固定 1500:此前每本书的合成音频被写死的 1500 条上限卡住
(TTS_CACHE_MAX_FILES),且这个限制在界面上完全看不出来 —— 整本预热时前面章节的音频会被
静默淘汰,提示却照报全量句数。现在「设置 → 数据 → 管理听书缓存」里可选不限 / 1500 / 3000 / 6000,
每本书的用量按已用 / 额度显示;改完额度立刻在全部书籍上生效(下调时马上释放磁盘,不必等下一次
写入才淘汰)。额度存为readerx.ttsCacheLimit(0 = 不限,后端读同一份,上限截断到 200000)。
淘汰口径不变(按修改时间淘汰最旧,.mime元数据不占额度、删除时一并清理)。
Fixed
-
不开听书也不再后台合成音频:自定义(HTTP)语音源下,阅读页此前只要「朗读处于停止状态」
就自动预热当前章节窗口,于是只读书不听说的用户一进书就被逐句合成写进缓存(服务端凭空多出请求,
流量与额度都白花)。现在预热改为按需:本次阅读里起播(听书按钮 / 选区「朗读」)或打开听书面板
之后才预热,起播后继续随翻章预热当前章;离开阅读页即复位,下次只读书不再触发。 -
分页阅读时工具栏「上一章」回到上一章第一页:该按钮此前与「往回翻页跨章」共用同一个落位请求,
点一下会落到上一章的末页,与滚动模式(上一章开头)、目录跳章(章首)的口径都不一致。
现在按钮一律从上一章开头读起,落到末页只保留给「往回翻」本身;该请求还带上了目标章,
中途改跳别章或退出分页阅读时即作废,不会再让别的一章排版完就被顶到末页。 -
书源「网页登录」等待期间不再占用主线程:认证窗改由核心 crate 统一编排(会话先就绪、UA 对齐、
存储探针随请求下发),桌面登录窗口的窗口事件与页面求值都在工作线程上等待,点「完成」/ 关窗 /
超时三条路径都能稳定收尾。 -
听书跟读高亮不再随朗读推进:段内插图那次改动把段落文字交给
ParagraphContent渲染时,
renderMarkedText(...)被当成普通函数直接调用并作为循环回调的返回值,动态文本节点因此
归「只跑一次的循环回调」所有 —— 标记(marks)后来再变也不会重渲染。表现为开听书后正文里
的朗读句高亮停在首次渲染的那一句(甚至整段没有高亮),而书签下划线、搜索命中、拖选高亮
同样受影响。现在把该调用写成 JSX 表达式,标记重新获得自己的响应式属主,逐句跟读恢复;
段内插图节点仍按片段身份复用,不会因为每句高亮而重新解码。 -
长按选中文本后,选区菜单的按钮不再「点不动」:菜单条此前会在浏览器派发 click 之前重新定位
——pointerup与selectionchange都会触发一次对账,只要选区锚点的几何变了(原生选区被系统
改写或收起、正文因重渲染 / 排版位移),条就会在按下与抬起之间移位;click 的目标随即退化成
按下点与抬起点的公共祖先,按钮的onClick不执行,看起来正是「条跳到别处、这次点按白按,
再点一次才生效」。现在点按菜单条期间(pointerdown → click)位置被冻结、也不收起,click 派发
完才解冻并按当前选区对账(click 因故不来时由 400ms 兜底解冻);同一个选区的重复对账不再重建
菜单状态,不再无谓地重算一次位置。顺带修掉菜单条自身的横向滚动会把菜单收起的问题。