Skip to content

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 02:34
· 39 commits to main since this release

下载与安装

本页产物按平台与 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。
  • 批量下载正文可指定章节范围:此前下载面板只有「下载剩余全部」,想只补中间一段、或先下前几十章
    离线应急都做不到 —— 现在面板里多了一行下载范围:默认「全书」,点「起始章 / 结束章」打开章节
    抽屉挑选(可直接输入章节序号,也能在列表里点,尚未缓存的章标「未缓存」),即只下第 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 兜底解冻);同一个选区的重复对账不再重建
    菜单状态,不再无谓地重算一次位置。顺带修掉菜单条自身的横向滚动会把菜单收起的问题。