Skip to content

Releases: zilorn/readerx

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 02:34

下载与安装

本页产物按平台与 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 兜底解冻);同一个选区的重复对账不再重建
    菜单状态,不再无谓地重算一次位置。顺带修掉菜单条自身的横向滚动会把菜单收起的问题。

v0.1.5

Choose a tag to compare

@github-actions github-actions released this 19 Sep 06:07

下载与安装

本页为 Android 安装包,按设备 CPU 架构拆分为 4 个 APK,选择匹配的一个下载即可:

  • arm64-v8a:近几年的主流手机 / 平板(64 位),绝大多数设备选这个
  • armeabi-v7a:仅支持 32 位应用的老设备
  • x86_64 / x86:Android 模拟器(分别对应 64 / 32 位镜像)

系统要求 Android 7.0(API 24)及以上。

直接覆盖安装即可升级,书架、书源、设置等数据保留在设备本地,不受影响。

使用中遇到问题欢迎到 Issues 反馈,请附上设备型号与 Android 版本。

以下为本版本更新内容:

Added

  • 设置 → 关于新增「开源库声明」:随包使用的第三方开源库(前端 solid-js / pdf.js / opencc-js
    等,后端 Rust 的 tauri / boa / reqwest / RustCrypto 等)连同各自许可与在 ReaderX 中的用途,
    整理成独立的 THIRD-PARTY-NOTICES.md,与 LICENSE 一样作为 Tauri resource 打进安装包,
    由 Rust 侧新命令 readerx_third_party_notices 读入(Android 走 asset 资源目录),
    在设置页最后一栏点开即可查看全文;两份随包文本(许可 / 声明)共用同一个全屏文本弹层组件。

  • 支持导入 PDF:本地导入(书架 +)与 WebDAV 导入都能选 .pdf 了,导入后与其他本地书一样
    进书架、可阅读 / 加书签 / 加入分组。解析走 pdf.js(只在真正导入 PDF 时才载入,不进首屏):

    • 文字层优先:按文字片段的坐标还原行与段落(中文不插空格、西文按间距补空格、行尾连字符
      合并回一个词),页眉页脚按「跨页重复出现 + 位于版心之外」两个条件一起判定后剔除,
      正文里引用的书名不会被误删;
    • 分章跟随 PDF 自带书签(多级大纲逐层纳入,条目过多时只保留上层),没有可用大纲时按累计
      正文字数分章(整本扫描件按固定页数分章);章节标题带「第 N–M 页」便于定位,
      大纲第一条之前的书名页 / 版权页并进第一章;
    • 扫描件兜底:整本几乎没有文字层时,每一页渲染成图片阅读;文字很少但画了图的页
      (封面、影印插页)同样按图渲染。页面图在导入时落盘到应用数据目录(images/),
      书籍 JSON 里只留文件名,渲染经既有的 readerx-img 协议直读,删除书籍时一并清理;
    • 多了一本 PDF 不影响已有书籍:只新增 format: "pdf" 这一种取值,旧数据照常读取。
  • 段内插图(文字内嵌图片):段落里的图片不再被拆出来独占一行,而是留在文字流里与文字同排 ——
    EPUB 里写在段落中的 <img>(含 SVG <image xlink:href>)与书源 HTML 里穿插在文字间的 <img>
    都保留原位置:图片插在段落文本的第几个字符之前(ChapterBlock.imgs 的 at 锚点,图片本身不占
    字符
    ),小图随文字同行、图比栏宽时按栏宽等比缩放并在段内自然换行,<p>他指着<img>说道</p>
    不再被切成「文字段 + 图片块 + 文字段」;一段里只有图没有文字时仍按整行插图排版(漫画 / 扫图章节
    的既有口径不变)。分页引擎按同一套行内样式测量含图段落(前缀高度对字符数仍单调,二分切分照旧),
    段内图与整行图共用全部图片基础设施:按书源会话下载、落成本地文件、readerx-img 协议直读、
    未就绪转圈占位、失败可就地「重试」、窗口预取与「下载全部正文」一并覆盖,删除书籍时一起清理。
    段落内图片不计入字符,书签 / 进度 / 搜索 / 朗读的偏移与旧版完全一致(旧进度仍指向同一段文字);
    文本替换与简繁转换会带着锚点一起重定位。兼容性:书源 schemaVersion 仍为 1(老书源零改动,
    返回纯文本 / 对象 { text, images } 的行为不变),纯文字段落不写 imgs 字段,旧版客户端忽略它
    即可;旧数据里以 data URL 内嵌的段内图在打开书籍时同样自动迁移成图片文件(book_images
    的迁移与自愈现在覆盖 img 块与 p 块的 imgs 锚点);本地 EPUB 需重新导入才按新口径排版
    (已导入的书不保留原文件,旧数据照原样渲染)。书源侧的写法见
    docs/book-source-image.md。

  • 书源登录支持「不用 Cookie 记登录信息」的站点(localStorage / sessionStorage / IndexedDB):
    一部分站点把登录凭证(JWT、uid + token)写在 localStorage 里而不是 Cookie 里,此前只抓
    Cookie 的登录浮层在这些站点上等于没登录。现在点「完成」时(以及自动网页认证刷新时)会顺带
    采集存储快照
    :Android 应用内浮层用页面探针抓 localStorage / sessionStorage 与 IndexedDB 的
    库名 / 对象仓清单,CLI 的 --auth cdp 走 CDP 原生 DOMStorage 逐 origin 读取(localStorage
    被页面改写也读得到),--auth webkit 走同一份探针;快照与 Cookie 一起写进该书源的登录态文件
    (不随书源 JSON 导出/分享,应用重启自动注入),书源代码用新接口 webview.storage()
    读取({ url, origin, localStorage: {…}, sessionStorage: {…}, indexedDb: […], updatedAt },
    只读、不触发认证,没有快照时返回空对象),把 token 显式放进请求头 / 参数即可。
    IndexedDB 只记结构不记记录内容(记录往往是整表业务数据,体积与隐私都不适合放进登录态),
    每个 origin 每类最多 500 条、单值最多 8192 字符(超出截断)。新增 CLI
    auth storage [--reveal] 查看快照(默认只显示值的前 8 个字符,避免 token 进了终端与 CI 日志),
    auth show 也会提示已保存多少项;书源编辑页的登录提示同时报告 Cookie 与存储条数。
    登录态文件是增量扩展的(旧文件只有 url / cookie / updated_at 也照常读写,App 与 CLI
    仍能交替使用同一份数据目录),且「只导入 Cookie」的操作不会抹掉已抓到的存储快照(反之亦然)。

  • 主 Tab 与 WebDAV 导入页改为常驻(保活)页面,切页不再丢状态:书架 / 发现 / 设置三个主 Tab
    与「WebDAV 导入」页现在由页面栈按 KEPT_PAGES 注册表(src/App.tsx)直接挂载并常驻 DOM,
    切走只是隐藏、再进入复用同一层 —— 搜索词、结果列表、分组筛选、勾选与滚动位置全部原样保留,
    不再重新挂载:此前每次切 Tab、或从阅读页返回 WebDAV 导入页,页面都会被卸载重建,跑到一半的
    搜索被丢弃、浏览到的目录回到根目录、勾选与滚动位置一并清零。这些路径因此不再出现在路由表里
    (路由只声明真正会 push / pop 的次级页面)。其余页面(阅读页、书源、详情等)仍按原来的
    推入 / 弹出逻辑卸载,它们离场时改用一份滚动区快照参与退出动画,滑出 / 淡出看到的仍是离开时的
    那一页(此前过渡中两层会互相抢同一份路由内容,旧页时而变成新页的副本、时而空白)。
    挂在 <Portal> 上的弹层(导入菜单、WebDAV 配置抽屉、在线书详情抽屉、重新导入弹窗)
    在离开所属页面时自动收起,不会残留在别的页面上。

  • 书源支持粘贴导入:书源管理页右上角新增「粘贴」(剪贴板)图标,书源列表为空时的
    空状态里也有「粘贴导入」按钮——打开抽屉即静默读一次剪贴板预填,也可以直接在输入框里
    粘贴 / 手打 JSON(单条或数组);输入框里放的是 http(s) 网址时按钮变为「拉取并导入」,
    联网取回即可,手里是网址还是 JSON 不必先分辨。JSON 解析不了 / 没识别到书源 / 拉取失败
    都在抽屉里就地提示,不再出现「点了导入却什么都没发生」。此前粘贴入口只藏在「确认导入」
    弹层底部,必须先选文件或拉一次网址才能看到,等于进不去。

  • 书源分组:书源管理页顶部新增分组筛选条(全部 / 未分组 / 各自定义分组,带数量),
    长按分组标签或点尾部「分组」进入分组管理(新建 / 重命名 / 删除);书源行右侧的文件夹按钮
    (编辑页「书源信息 → 分组」同理)把书源归入分组,分组管理里还能对整组一键启用 / 停用,
    只改写状态不一致的书源。删除分组时由 Rust 侧整批清掉组内书源的归属(退回未分组),
    不留指向已删分组的悬空引用。

  • 书源管理页支持长按多选:长按书源行进入多选(该行随即选中),此后点击行即勾选 / 取消勾选,
    页头显示已选数量与「全选」——全选按当前筛选来,只作用于此时可见的书源(配合分组筛选即可
    「先筛出一组再全选」)。选中后底部操作条可批量启用 / 停用(只改写状态不一致的书源)、
    一起归入 / 移出分组、导出为一个 JSON 数组(复制到剪贴板,便于分享 / 备份)与删除
    (连点两次确认);操作完成后自动退出多选。

  • 发现页按分组限定书源:筛选条与书源管理页共用同一个(会被记住的)选中值,搜索与「发现」
    都只在该分组的已启用书源里跑,数量按当前模式统计;切分组后当前发现源不在范围内时
    自动收起它的分类与结果,避免「加载更多」继续向范围外的书源要数据。

  • 分组随书源 JSON 导入 / 导出:备份 / 分享只带可读的 groupName(不带本机分组 id),
    导入时按名字匹配本机分组(没有则新建),确认页列出将「新建 / 已有」的分组并可整体关闭
    「按分组名导入分组」——关闭后新导书源落未分组,覆盖已有书源时保留本机原有的分组。

  • 在线书图片预取:正文按「当前章 ±5 章」窗口预取,这一圈章节的正文都取回之后,
    阅读器接着把窗口内已下载章节里还没取过的图片也取回来(读到哪一章就先取哪一章),
    顺序往下读时图片通常已经在本地;预取失败即放弃(不在后台反复重试、不计入章节失败),
    用户读到该章时阅读路径会再取一次 —— 预取失败的图片不会被失败冷却挡住,
    占位框上还有「重试」。图片预取与正文预取共用同一个下载闸与同址去重,不会重复请求;
    用户跳章 / 听书跨章时立即改以新章为中心,批量下载开始时让位,「停止下载」即时停下
    (同一章不会刚停就自动重启),下载面板按阶段显示图片进度。图片预取在正文预取结束后
    才开跑,因此不会挤占正在读的那一章的正文加载。

  • 书源加密工具补齐 SHA-256 / HMAC / AES-256-GCM:cryptoUtil 在原有的 MD5 / SHA-1
    之上新增 sha256、hmac(algorithm, key, data, encoding?)(MD5 / SHA-1 / SHA-256)与
    aesGcmEncrypt / aesGcmDecrypt(AES-256-GCM,key 32 字节、iv 12 字节,未给 IV 时
    随机生成,支持 AAD,密文尾部按 WebCrypto 约定带 16 字节认证标签),摘要与 HMAC 可输出
    hex(默认)或 base64;另加 hexEncode / hexDecode 两个十六进制工具。base64.decode 与
    hexDecode 改为字节保留字符串(1 字符 = 1 字节),解码出的密钥 / 密文可以直接喂回
    cryptoUtil 或拼进请求体,不再被 UTF-8 往返改写;密钥 / IV 支持 base64、hex、UTF-8
    字面量三种写法(按字节数精确匹配,长度不对直接报错,不做静默填充或截断)。
    规范见 docs/book-source-api.md。

  • 书源引擎可编译为独立二进制 readerx-source:书源引擎(Boa 沙箱 + 宿主 API)与书源
    持久化抽成独立 crate src-tauri/crates/readerx-source,App 与命令行共用同一份代码;
    不启动应用即可跑规则——离线检查书源文件、调单个入口函数、端到端跑「搜索 → 目录 → 正文」。
    子命令:sources / call / run / test / auth / login,支持 --json 机器输出与
    --verbose 打印书源 console 日志;--data-dir 可指向 App 的数据目录,直接跑已安装的书源
    与其登录态。用法见 docs/book-source-cli.md。

  • 独立二进制的浏览器标志与登录态支持:--ua / --header / --cookie / --cookie-file
    (Netscape cookies.txt、EditThisCookie / Cookie-Editor JSON、Playwright storageState、
    DevTools 请求头文本,按内容自动识别),以及带域名作用域的 Cookie 导入——浏览器里存着
    几十个站点的 Cookie,只按请求域名 / 路径 / https 逐条筛选发送,不再整行无条件外发。

  • 独立二进制可在真实浏览器内核里过 Cloudflare 挑战 / 登录:auth webkit 用系统
    WebKitGTK 打开页面(与 App 同内核,无头可用 xvfb-run),auth cdp 连已有 Chrome 的
    DevTools 端口取 Cookie(--browser 也可由本工具拉起浏览器)。抓到的 Cookie(含 httpOnly 的
    cf_clearance)覆盖式保存为该源登录态并立即注入会话;auth show / auth clear 查看与清理。

Changed

  • 「确认导入」弹层底部的「从剪贴板粘贴导入」改为「继续粘贴导入」按钮:点它退回粘贴抽屉换一份
    内容重新解析,不必先关掉弹层再从头进。
  • 导入相关的界面拆成独立组件:SourcePasteImportSheet(粘贴抽屉)与
    SourceImportConfirmSheet(确认导入),三条导入路径共用同一份确认界面,书源管理页只留状态与流程。
  • 图片下载失败(或被清理掉的本地副本读不出来)时的占位框改为把「重试」按钮放在
    占位文字的右边(同一行),不再另起一行。
  • 构建产物瘦身:简繁转换词典改为按需取用的压缩资源(新增 scripts/han-dict.mjs 与
    src/lib/hanDict.ts):opencc-js 把词典以 JS 字符串字面量发布,简→繁那一份打出来是
    1.1 MB 的 JS chunk(构建时触发 Vite「chunk 大于 500 kB」告警),WebView 每次都要下载并解析
    这么大一坨模块。词典本质是纯数据,现改为构建期 gzip 成 src/generated/han-dict-<方向>.bin
    (简→繁 450 KB、繁→简 28 KB),运行时按方向取回、解压(优先内核 DecompressionStream,
    老内核回退 fflate)后再用 opencc-js 建树;未开启简繁转换的用户依旧完全不下载。
    转换结果与 opencc-js/cn2t、opencc-js/t2cn 逐字一致(已用约 900 万字语料双向比对)。
    词典产物由 vite.config.ts 的 hanDict 插件在 dev / build 启动时自动生成、不入库,
    词典未变不重复压缩;两个方向共用同一个 4 KB 小 chunk(opencc 核心 + 词典地址),
    不再各自复制一份核心代码。词典相关产物共省下约 700 KB。
  • 首屏不再下载 EPUB 解压库:src/lib/books.ts 对 ./epub 改为按需 import(),fflate 与
    EPUB 解析器移出首屏(入口 chunk 56.9 KB → 44.3 KB,gzip 21.9 KB → 16.4 KB),
    两者都只在真正导入电子书时才加载。
  • 在线书入架后可直接加入分组:在线书加入书架后,提示条改为「已放入书架」+ 橙色「加入分组」
    按钮,点它当场弹出「移入分组」抽屉(选已有分组、就地新建,也可移出分组或归入「隐藏」),
    不选分组就只是提示、什么都不改。抽屉挂在应用外壳上而不是发起入架的组件里,因此入架后
    已经跳到阅读页 / 书架、或书籍详情抽屉已换过书,按钮依然可用;分组结果照旧走
    readerx_book_patch_meta 就地落盘,书架按分组筛选时立刻能看到。此前「已加入书架」的提示
    被书籍详情抽屉的遮罩压在下面,在「发现 → 搜到书 → 加入书架」这条最常用路径上根本看不见;
    提示层级现在提到抽屉之上,且带按钮的提示停留更久(6 秒)并改用卡片样式(原深色胶囊上
    橙色按钮字看不清)。抽屉标题随「加入分组」的说法统一为「移入分组」(书架多选入口同样用它)。
  • 听书定时不再被「停止朗读」一并清掉:定时(分钟 / 本章结束)此前绑在单次朗读会话上,
    stop() 会把它复位 —— 正在定时朗读时用选中文字菜单点「朗读」,内部动作是「先停旧会话、
    再从句首起播」,于是刚设好的定时被静默取消(打开面板已是「关闭」)。现在停止朗读只结束
    这一轮播放,定时原样保留:分钟倒计时在停止期间冻结、重新起播接着走(剩余时间照常显示),
    「本章结束」留给下一次朗读;只有面板里选「关闭」或真的到点才解除。

Removed

  • 阅读菜单进度条上的「逐页刻度」:进度条上按本章页数等分的灰色圆点与阅读设置里的对应
    开关一并移除(进度条本身、拖动跳页与「x/y页」提示都不受影响)。旧版本存过的
    readerx.menuSliderNodes 偏好会在启动时由后端删掉,不在应用数据目录里留下没人再读的
    状态文件;迁移幂等,删不掉也只是留个文件,不影响其它偏好。

Fixed

  • 登录态(Cookie 与存储快照)现在登录完立刻生效,不再需要重启:写入登录态的入口
    (http.setCookie / 存储快照缓存)要求「本书源会话已建立」,而应用内「网页登录」这条入口
    从不建会话 —— 于是「本进程还没调用过该书源就先登录」时,抓到的 Cookie 与 localStorage 快照
    只写进了文件、内存会话里没有,表现就是要重启一次才生效(重启后由「注入已保存登录态」补上)。
    现在这些写入入口会按需建立会话(已有会话不动,不会覆盖 CLI --ua / 身份文件 / 抓到的
    作用域 Cookie),认证入口也先把会话准备好;同一次登录的 Cookie 与快照一律先注入内存、
    再落盘(写盘失败只影响「重启后是否还在」,不影响本次登录),并补了两条回归测试。
  • 在线书「重新拉取书籍信息」现在也同步标签:该按钮此前只写回简介与封面,书源在
    bookDetail 里新增 / 改动的 tags 点刷新也不会同步(详情页标签区一直停在入架那一刻)。
    现按「书源来源 + 手编」两笔账合并:书籍记录新增 sourceTags(上次由书源写回的标签),
    刷新时保留手编标签、补上书源新增的、去掉书源已删除的,现有标签的相对顺序不变;
    入架时随书保存的标签一并记为书源来源。老数据没有这笔记录(无从区分手编与书源),
    首次刷新退化为并集 —— 只追加不删除,绝不丢用户手编的标签;bookDetail 这次没返回
    tags(或返回空数组)时不动书架上的标签。刷新结果提示与按钮说明同步改为「简介、封面与标签」。
  • 元信息补丁里的「清除」操作此前根本没落到磁盘:readerx_book_patch_meta 的双层 Option 字段
    (简介 / 封面 / 标签 / 分组)按 serde 默认行为解析时,显式 null 与「字段缺省」都会被解析成
    外层 None(= 不改动),于是「移除封面」「清空标签」「移出分组 / 移入隐藏」只改了 WebView
    内存里的那份,磁盘上的书籍 JSON 原封不动,重启后全部回退。现在用自定义反序列化区分两者:
    缺省 = 不改动,显式 null = 清除,空数组同样按清除处理;已补回归测试(含「老书籍 JSON
    没有新字段也能照常载入、且不被回写」)。
  • readerx-source auth clear 清不掉用 auth cookie / auth webkit / auth cdp 导入的登录态:
    只删了整行的 source_sessions/<id>.json,漏了带作用域的 profiles/<id>.json,而
    call / run / auth show 每次都会重新套用后者,表现为「提示已清空、Cookie 却还在」。
    现在两处文件都删,报告改为「整行 N + 作用域 M」的文件条数口径(并列出实际删除的文件,
    无登录态时明确提示),可重复执行。
  • readerx-source 的显示环境预检失效:DISPLAY=(空串,容器里常见)被 is_some() 当成
    「有显示」,于是无头环境一路走到 GTK 初始化才报错。现在空串按「没有显示」处理,
    在打开窗口前就给出「用 xvfb-run 或 --auth cdp」的可读提示。
  • readerx-source auth show 与 sources 的信息口径:
    • show 现在套用与 call / run 完全相同的身份(含 --profile / --ua),打印的
      头与 Cookie 就是这次请求真正会带上的(此前无视 --profile 的 UA);
    • sources 的「登录态 N 条」此前只统计整行文件,用带域...
Read more

v0.1.4

Choose a tag to compare

@github-actions github-actions released this 12 Sep 13:10

下载与安装

本页为 Android 安装包,按设备 CPU 架构拆分为 4 个 APK,选择匹配的一个下载即可:

  • arm64-v8a:近几年的主流手机 / 平板(64 位),绝大多数设备选这个
  • armeabi-v7a:仅支持 32 位应用的老设备
  • x86_64 / x86:Android 模拟器(分别对应 64 / 32 位镜像)

系统要求 Android 7.0(API 24)及以上。

直接覆盖安装即可升级,书架、书源、设置等数据保留在设备本地,不受影响。

使用中遇到问题欢迎到 Issues 反馈,请附上设备型号与 Android 版本。

以下为本版本更新内容:

Added

  • 书源编辑页重排为常驻三 Tab(书源信息 / JS代码 / 测试):切 Tab 不丢草稿,
    页头保存按钮与 Tab 栏始终在位,长表单(信息 / 测试)在各自 Tab 内滚动;
    • JS代码:编辑器占满整页剩余空间,带行号槽与语法高亮(关键字 / 字符串 /
      模板串与 ${} 插值 / 正则 / 数字 / 注释 / 书源宿主 API http·html·util·
      base64·cryptoUtil·webview·console 单独配色);长行横向滚动不折行,
      滚动时行号与高亮层同步,配色随浅色 / 深色 / 护眼主题切换;
      输入层是原生输入框,中文输入法与长按选词照常;
    • 「填入模板」在已有代码时需再点一次确认,避免一键抹掉正在写的内容;
    • 入口函数与测试参数在切 Tab 后仍保留(不会切走再切回就被重置)。
  • 处理不了的异常会明确提示用户(不再只写日志 / 控制台):
    • Rust 侧任何漏网的 panic(书源引擎、第三方解析库、命令线程……)都会经 panic hook
      以 readerx-internal-error 事件推给前端,弹出错误提示条并附「消息 + 位置」,停留 8 秒;
    • 前端接管全局未捕获异常(window.onerror)与未处理的 Promise 拒绝(unhandledrejection);
    • 后端读写本地设置 / 书籍 / 书源列表 / 正文拉取 / 登录状态清除等原先只 console.error
      后静默降级的路径,改为把真实原因提示给用户(相同文案去重并按重复次数退避,
      批量下载 / 持续写盘失败不会刷屏);
    • 「本地设置载入失败」「书库载入失败」也会提示,同时仍以内置默认值继续渲染,不再出现
      「数据凭空没了但没有任何说明」的情况;
    • 网页登录的 Cookie 持久化失败不再只写日志:登录提示会说明「Cookie 保存失败(重启后需重新登录)」。
  • 书源支持网络导入:书源管理页右上角新增「链接」图标(空列表时也有「从网址导入」按钮),
    粘贴一个指向书源 JSON(单个或数组)的 http/https 网址即可拉取并进入原有「确认导入」流程
    (冲突覆盖逐条可选、含免责声明),无需先下载到本地再选文件;网址不合法 / 拉取失败 /
    内容无法解析时会就地提示原因。
  • 在线书「书籍详情」页右上角新增刷新按钮(仅在线书显示):可随时重新拉取书籍信息——重调书源 bookDetail 取回最新简介与封面(封面仍经书源会话下载压缩成缩略图落盘),书源在详情里新补充 / 更新的简介与封面无需重新入架即可同步;刷新中按钮转圈禁用,结果以轻提示反馈。
  • 标签可点击触发「发现」快速搜索:书籍详情页(/detail/:id)与发现页在线书详情抽屉里的只读标签点击后,会切到发现页「搜索」模式并以该标签为关键词自动跑一次全源搜索(从详情页进入时跳转 /discover?q=标签),方便按题材/关键词找同类书;编辑态带移除按钮的标签不受影响。
  • 书源封面(BookItem.cover 可选字段,自动兜底):书源在 searchBook / discoverBooks / bookDetail 里返回书籍封面绝对地址后,「发现」的搜索结果 / 分类发现列表行与「书籍详情」预览会展示真实封面;「加入书架」时应用经该书源会话(Cookie / UA / 默认请求头,Referer 取书页地址 bookUrl)下载封面并压缩为缩略图随书保存,书架与书籍详情页显示真实封面——带防盗链或需登录的图站同样适用。书源未返回封面、地址非法或下载失败时自动回退书名首字渐变占位封面,不影响搜索 / 预览 / 入架;列表行封面采用「滚入视口附近才下载」的懒加载,封面下载并发受全局「书源并发」设置约束,同一封面一次会话内复用下载结果。
  • 在线书「书籍详情」页新增「书源地址」行(仅在线书展示):显示该书在书源网站的书页 URL,点击该行即用系统浏览器打开书源原网页(Tauri 端走系统默认浏览器,网页预览下退化为新标签页),便于到书源站点核对正版页面与更新。
  • 书源新增「自动网页认证」(书源 JSON autoAuth,默认开启,编辑页「网页登录」卡片内可单独关闭):
    http.* 请求命中 Cloudflare 人机挑战(或 cf_clearance 令牌过期后再次挑战)时,引擎在 Android 端
    自动拉起应用内 WebView 完成认证、覆盖式刷新并按书源持久化 Cookie,然后自动重试原请求一次
    (书源代码无需改动,普通 http.get 即可);两次自动弹窗间约 45s 全局冷却,避免批量下载/并发搜索
    连环弹窗。未弹窗/被取消/刷新后仍被拦截时,原挑战响应带 cf 字段(auto:disabled/unsupported/
    cooldown/cancelled/stale)返回;书源代码显式 webview.login(url) 也受该书源 autoAuth 开关
    约束(关闭时返回 ok:false),编辑页手动「网页登录」不受影响。桌面/iOS/浏览器预览不支持时静默降级,
    详见 docs/cloudflare.md 与 docs/book-source-api.md。
  • 书源作品信息支持 tags 标签字段(searchBook / discoverBooks / bookDetail 返回的 BookItem 可带 tags 字符串数组,自动去重去空白,单个 ≤ 24 字、至多 30 个):发现页点击结果弹出的「书籍详情」预览会展示标签,「加入书架」时随书保存;所有书籍(含在线书)都可在书籍详情页查看,并通过右上角「编辑」增删标签(本地导入的书同样适用)。
  • 在线书正文图片改为阅读时按需下载(新增 src/lib/chapterImages.ts):拉取章节正文只解析并保存图片地址,正文立刻落盘、拉取速度与纯文字章一致;用户读到该章时才经书源会话逐张取回并写回本地章节(离线可读、本次运行内不重复请求,下载并发受「书源并发」约束、最多 4 路)。分页排版先等本章图片就绪(最多 8s,超时先按占位把正文给用户、图片到位后再排一次),避免先按占位高度排一遍、图片到位后又整章跳动;等待期间覆盖层显示「正在获取图片 x / y」。
  • 在线书图片缺失可再次尝试获取:下载失败的图片在正文里显示带「重试」按钮的占位框(点一下忽略冷却立即重取,成功后本章按真实尺寸重排);同一张图再次读到本章时也会自动再试一次,两次自动尝试之间约 4s 冷却,避免连环打图站。
  • 「下载剩余全部」改为正文与图片分两趟:先把所有章节正文下完,再单独过一遍图片(不与获取章节同时取图),下载面板分别显示两阶段进度与失败张数;图片失败不影响章节正文的下载结果。
  • 简繁转换(新增 src/lib/hanConvert.ts 与 src/lib/hanDisplay.ts):设置页「阅读」区块与阅读器「阅读设置」面板都可一键切换「关闭 / 简→繁 / 繁→简」,选定后应用内书籍文字自动换成对应字形 —— 书名、作者、简介、标签、目录章节名、阅读页正文与页眉页脚、书架卡片、书籍详情、发现页的搜索结果与分类列表、在线书详情预览(简介 / 目录 / 标签)、书架搜索一并生效;听书朗读与全书搜索读到的也是转换后的同一份文字。
    转换只作用于展示副本:书库原文、书源返回的数据、入架落盘内容与用户手输内容(书籍编辑表单、文本替换规则)都不受影响,随时切回「关闭」即可还原。词典来自 opencc-js(OpenCC 的纯 JS 实现,MIT / Apache-2.0),简→繁约 1.1 MB、繁→简约 110 KB,按方向动态加载,未开启的用户不下载也不解析;章节正文按需惰性转换(读到哪一章才转哪一章),打开大书不会先卡一次,目录、听书、全书搜索也都只转换真正访问到的章节。

Fixed

  • 有章节在下载时,批量下载 / 重新加载本章不会被挡住:正文拉取从「每本书同一时刻只允许一个任务」
    改为各自独立、可并存,冲突一律以用户操作为准:

    • 下载面板的「下载剩余全部」随时可点:开始后后台窗口预取让位(它覆盖的窗口章节本就在批量目标里,
      且批量下载继续按「阅读焦点」优先取回正在读的那一章),不再出现按钮显示「下载中…」点不动的情况;
    • 「重新加载本章」在窗口预取 / 批量下载进行中同样可用(同一章正在重载时不重复发起);正在重载的
      那一章其余任务跳过,重载结果也不会被随后返回的旧拉取覆盖;
    • 「检查书籍更新」不再要求先停下下载:末尾追加照常执行;结构冲突选「覆盖更新」时自动停掉在跑的
      章节拉取,并让已发出、随后才回来的正文回写作废(不会写到重排后的错位章节上);
    • 书籍详情页「重新拉取书籍信息」同样不再被章节下载拦住(它只改元信息,不影响正文);
    • 底层配套:同一本书的写盘按书 id 排队串行(Rust 侧章节回写与元信息补丁都是「读文件 → 改 →
      整本写回」,并发执行会互相丢掉对方刚写好的内容),且每笔写在队列内基于最新缓存 + 最新目录
      复核后再套用补丁;正文回写改为先到先得,只有用户显式「重新加载本章」才覆盖已有正文,
      图片本地副本的回写改为与最新正文合并(与重载并发时不会拿旧正文覆盖新正文)。
  • 修复「图片多的在线书加载时应用直接崩溃」:此前正文图片下载后是以 data URL(base64)写进书籍
    JSON
    的,同一张图会同时存在于磁盘 JSON、Rust 解析出的字符串、IPC 报文、WebView 的 JS 字符串,
    并在排版时被逐张解码 —— 一章几百张图时内存成倍膨胀(一本书的 JSON 甚至能到几百 MB),
    打开书 / 应用启动扫描书库时会直接被系统杀掉。现在改为:

    • 图片字节由 Rust 写成本地图片文件(应用数据目录 images/<书籍id>_<地址哈希>.<扩展名>),
      章节块只保留文件名引用,书籍 JSON 与 IPC 报文都只带少量文本,内存占用与图片数量解耦;
    • 渲染经应用内自定义协议 readerx-img 直接从文件读取(离线可读),图片按 loading="lazy"
      惰性解码,图片被重新下载时才重新请求;
    • 排版所需的图片尺寸由 Rust 读文件头解析(JPEG / PNG / GIF / BMP / WebP),不再为了量尺寸把
      整章图片解码一遍;只有尺寸确实未知的图(老 data URL / SVG)才退回解码,且限 3 路并发;
    • 书库元数据扫描跳过图片载荷(流式解析),应用启动不再为书库里的图片字节分配内存;
    • 旧数据自动迁移:已存在的书籍(含 EPUB 插图)在打开 / 回写时把章节里的 data URL 图片抽成文件,
      超大书籍文件走流式重写(内存占用只与单张图片同级),迁移失败则原样保留、不丢图;
    • 顺带修复:Rust 侧回写书籍时会丢掉图片块的 remote 字段(导致下载过的图无法重试);
      书籍文件改为「先写临时文件再原子替换」,写到一半崩溃不会再留下半截书文件。
  • Rust 端不再「直接抛异常」把应用打崩:全面梳理了会 panic 的路径,改为返回可读错误或安全兜底 ——
    远端响应头 Content-Type 含非 ASCII 字符(如 İcharset=)时,旧实现拿 to_lowercase 得到的下标去切原串,
    会越界 panic(任何站点都能触发),现改为等长的 ASCII 小写匹配;HTTP 方法解析、书源 HTML 转纯文本
    等所有可能越界的切片都改成安全取值。书源 JS 执行线程与全部 IPC 命令统一加了 panic 兜底
    (release 由 panic = "abort" 改为 unwind + catch_unwind):引擎 / 第三方解析库万一 panic,
    也只让这一次调用失败并把 …内部异常: 原因 回传前端,其它章节、其它书源不受影响;
    同时安装 panic hook,把「消息 + 位置」打到 logcat 并推给前端提示(见上「Added」)。

  • 书源死循环 / 无限递归不再卡死界面:Boa 引擎增加单个函数帧的循环次数上限(约 1 亿次),并把引擎的
    运行时限制翻译成中文原因 —— while (true) {} 这类规则现在返回「循环次数超出上限(书源代码可能存在死循环)」,
    而不是让引擎线程被永久占住、界面一直转圈(说明同步更新到 docs/book-source-spec.md 与 docs/book-source-api.md)。

  • 无 charset 的短响应体不再整段乱码:响应解码「合法 UTF-8 优先」的判断里,替代符计数被 max(1) 固定,
    导致长度不足约 334 字符的无 charset 响应(返回中文书名的 JSON 接口 / 小页面很常见)被误判成 GB18030;
    现在只要整段是合法 UTF-8 就直接按 UTF-8 解码,与文档描述一致。

  • 批量拉正文时单个 worker 异常不再让整批结果丢失:未领取的章节交给其它 worker 继续处理,最终没有写回结果的
    章节会带上真实原因,而不是笼统的「未知错误」。

  • 应用启动明显提速:此前首帧必须等 Rust 把整座书库的完整正文(每本书的全部章节 + 在线书内嵌 data URL 大图)读出来并整体搬进 WebView 才渲染,启动耗时随书库总字节数增长。现在启动只拉书库「元数据」(章节标题 + 每章字符数),外壳立即渲染、书库后台载入;书架 / 搜索 / 详情等页面直接消费元数据展示封面与进度,单本正文在打开阅读页时才按需读取。Rust 侧新增 readerx_book_list_meta / readerx_book_get / readerx_book_patch_meta:元数据只带章节头,正文按需单本读取,分组 / 书名 / 封面等编辑在磁盘就地打补丁,正文不再整本经 IPC 往返。

  • 修复新增 / 改动书源后不立即生效、必须重启软件的问题:书源管理页的「启停开关」与「JSON 导入(新增 / 覆盖)」此前直接写入后端文件,却没有同步进程内的书源清单(该清单只在应用启动时加载一次),导致改动要重启后才在「书源管理 / 发现」页体现。现在每次启停、导入完成后都会立即重拉书源清单,新增、覆盖、停用的书源无需重启即时生效。

  • Android 端禁用 WebView 页面手势缩放:双指捏合 / 双击不再把整页放大错乱(首页 viewport meta 增加 maximum-scale=1, user-scalable=no),阅读字号仍由应用内设置调节。

  • tts悬浮球播放键图标异常。

  • 「重新加载本章」现在全过程可见:此前点下去没有任何反馈(设置面板收起、正文照旧),用户无法判断是否在加载 —— 现在阅读区立即盖上「正在重新加载本章…」覆盖层(带取消入口,覆盖期间不翻页 / 不误呼菜单),设置面板里的该行也同步转圈并显示「正在重新加载本章…」;加载成功提示「已重新加载「章节名」」,失败则回退到重载前的阅读进度(分页页码与段落文字偏移双保险)并提示失败原因,不再静默停在原位。

  • 在线书下载正文时阅读页不再高频闪烁:此前阅读器读的是整本书对象,后台每落盘一章都会换掉整本书 → 阅读区重新测量尺寸并写入一个新的面积对象 → 排版参数失效 → 当前章整章重新分页(页面闪断、偶发「正在加载」),批量下载时尤其明显。现在阅读视图只读取「当前章 ±1 章」这份窗口视图(src/lib/renderWindow.ts):窗口内三章与书籍元信息都没变时沿用上一次的书对象引用,章节内容、分页、进度百分比、面积测量整条渲染链路全部跳过;面积测量也改为尺寸没变就不写信号。目录 / 下载面板 / 全书搜索 / 听书引擎仍读实时书库,下载状态与跨章朗读不受影响。

  • 章节头与每章字符数改为按章节对象缓存复用(未变章节保持同一引用),正文回写对书架可见元数据的发布按 ~250ms 合并、可见数据没变时整次跳过 —— 此前每落盘一章都要按全书正文重算一遍字数并发布一次整本书库清单。阅读器的显示级文本替换同样按章节缓存,下载期间不再反复重跑全书替换规则。

  • 修复阅读页正文加载期间点屏幕中间呼不出菜单:正文未就绪时盖在阅读区上的两层覆盖层(大章节「正在加载…」与在线书「正在获取章节正文…」)会吞掉按下 / 抬起事件,等待期间菜单完全无法呼出 —— 而在抓正文往往要等几秒,菜单正是这段时间里看目录 / 下载 / 改设置或返回的唯一入口。现在覆盖层不再拦截手势,翻页约束改由手势判定承担:正文未就绪时点中间照常呼出菜单,左右区域与横向滑动翻页仍按原门闩语义被挡住(正文没就绪时自绘选区引擎本就停用,不会误起选)。

  • 在线书读到哪一章就先出哪一章:此前阅读窗口预取(当前章 ±5 章)把整个窗口一次性交给书源引擎取回,要等窗口内所有章节都拉回来才开始写回书库,正在读的这一章因此必须陪跑其余章节的预取(跳章后尤其明显)。现在当前阅读章单独请求、取回即落盘,阅读器拿到本章正文直接显示,其余窗口章节随后按小批(约 2×书源并发)后台补齐、逐章落盘;拉取任务在跑时切章也不用等它跑完 —— 窗口预取会在下一批之后改以新章为中心先取本章,批量下载也会把正在阅读的未缓存章节提前单独取回。

Changed

  • 发现页搜索「出一个书源显示一个」:此前要等所有书源跑完(Promise.all + 末尾按书源名排序)才一次性列出结果,慢源 / 超时源会拖住全部结果。现在每个书源一返回就把它的结果追加到列表上屏,搜索进度条与结果同屏共存(有结果时缩短为一行),底部计数显示「已找到 N 条 · 仍在搜索其他书源…」;列表顺序即书源返回顺序,不再在结束时重排,避免结果行在用户点击时跳动。同时新增搜索开始清空上一次结果、被新搜索接管后旧书源的回包直接丢弃。
  • 在线书的两级窗口明确分工:预取窗口保持「当前章 ±5 章」(后台提前抓 11 章落盘,顺序阅读与听书跨章不断档),渲染窗口为「当前章 ±1 章」(阅读视图实际读取的范围)。窗口外的正文回写只落盘、不惊动前端渲染。
  • 在线书章节 img 块新增可选字段 local(本地副本文件名):图片字节改为存在应用数据目录的
    images/ 下,章节只留引用(src 保持原始网络地址、remote 保持图片身份),渲染由
    readerx-img 自定义协议直读文件;旧数据启动后首次打开 / 回写时自动迁移,书源 schemaVersion 不变。
  • 在线书章节 img 块新增可选字段 remote(图片原始网络地址):src 仍是可直接渲染的地址(未下载时即网络地址),阅读时下载 / 重试 / 本地副本对应一律以 remote 为准,旧数据(只有 src)按原逻辑直接渲染,书源 schemaVersion 不变。

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 06 Sep 04:36

下载与安装

本页为 Android 安装包,按设备 CPU 架构拆分为 4 个 APK,选择匹配的一个下载即可:

  • arm64-v8a:近几年的主流手机 / 平板(64 位),绝大多数设备选这个
  • armeabi-v7a:仅支持 32 位应用的老设备
  • x86_64 / x86:Android 模拟器(分别对应 64 / 32 位镜像)

系统要求 Android 7.0(API 24)及以上。

直接覆盖安装即可升级,书架、书源、设置等数据保留在设备本地,不受影响。

使用中遇到问题欢迎到 Issues 反馈,请附上设备型号与 Android 版本。

以下为本版本更新内容:

Added

  • 页面切换动画:进入更深页面(书架/设置等 → 详情、书源、阅读页)时旧页同步向左滑出、新页自右滑入;返回(页面返回键 / 系统返回)时反向滑回;书架 / 发现 / 设置三个主 Tab 之间切换为整页淡入淡出(含页面顶部标题栏)——淡入会在新页正文真正挂载后再开始,避免先闪空白/加载占位、标题栏在动画结束后才突然切换。遵守系统「减少动态效果」偏好,不影响阅读器内部翻页动画。
  • 书架新增内置「隐藏」分组:把书移入后,该书不再出现在「全部」、来源筛选、自定义分组与书架搜索里,只能通过筛选条里的「隐藏」tag 查看(该 tag 排在各自定义分组之前)。
  • 书架顶部选中的筛选标签(全部 / 来源 / 分组)会被记住,下次进入软件直接停留在该标签。
  • 左右翻页模式下,阅读菜单底部按钮上方新增悬浮的章节页进度条:可点按 / 拖动跳转到本章任意一页,拖动中显示「x/y页」提示(听书悬浮球保持在进度条上方);进度条上的「逐页刻度」(按页数等分的灰色圆点)与进度条本身都可在阅读设置中开启 / 关闭。
  • 所有需要纵向滚动的页面 / 弹层(整页滚动区、阅读器目录/书签/全书搜索、听书与阅读设置、分组、WebDAV、许可文本等)接入移动端风格的自定义滚动条:内容可滚动时右侧浮现细滚动条,静止后自动淡出,可按住轨道或拖动拇指快速定位(拖动中拇指放大)。
  • 发现页点击搜索结果 / 发现列表的书籍后改为呼出底部「书籍详情」抽屉(不切路由,保留发现页搜索词、结果与滚动状态);抽屉内自动拉取书源「详情」与「目录」,可直接预览简介、章节列表,并选择「加入书架」或「加入书架并阅读」。
  • 书源正文支持图片(图片正文):bookContent 返回含 <img> 的 HTML 或对象 { text?, images? } 即可在正文中展示插图 / 整章图片(漫画、扫描、图文混排);图片经该书源 HTTP 会话(Cookie / 默认头 / UA,Referer 取正文页地址)下载为 data URL 随章节本地缓存,离线可读、对防盗链图站开箱即用。老书源纯文本返回与已缓存章节不受影响,书源 schema 与书籍数据格式均未变。
  • 在线阅读窗口拉取改为「当前章优先、逐章落盘」,含图章节打开时无需等整窗下载完。
  • 跳转书籍页数时显示返回原进度。
  • 书架页面全选。
  • 在线书「检查书籍更新」:阅读设置新增入口,点击重新获取书源目录并与书架目录比对——末尾新增章节自动追加并提示更新章节数;目录结构不一致(中段被新增 / 删除 / 重排或章节地址变动,无法安全追加)时弹窗询问是否整本覆盖更新,覆盖会保留章节地址未变章节的已缓存正文,其余按需重新获取。
  • 书源导入确认页会列出与本机重复(同名 · 同站点)的书源,默认用导入内容覆盖,可逐条关掉开关以保留本机版本;顶部计数与完成提示同步扣除被保留的条目。
  • 在线书「重新加载本章」改为先拉取新正文、成功后才覆盖落盘:正文变化可能导致本章书签失效时,先弹窗询问是否继续,取消则不改动正文与书签;拉取失败也保留本章原有正文(原实现会先清空缓存,失败后需重新获取)。

Fixed

  • 修复在线书(尤其含图片/大书)逐批下载正文时的明显卡顿与批量下载闪退:下载不再对整本做深拷贝、不再把整本 JSON(含 data URL 图片)反复经 IPC 传送,改为「只回写本次变动的章节」增量落盘(新增 Rust command),并在章节解析等耗时步骤之间让出主线程;阅读器在收到与当前章无关的正文回写时不再重置页码 / 重排当前章 / 闪「正在加载」。
  • 发现页重选分类 / 书源标签后,旧列表不再静默挂着,而是先清空并显示「加载中…」占位,等新列表返回后再替换。
  • TTS 自动跨章时,已取消「跟读跟随」的用户不再被强行切到新章节:朗读只在内部继续推进,阅读视图留在手动位置(点「返回跟读」仍可追回朗读所在章)。
  • 分页模式跨页选取文本时,选区端需在页缘外持续停留判定时长才会翻页续选,且每次拖出只翻一页、回页内重新拖出才再翻,避免短暂越界便连续跨过多个页面。
  • 分页模式跨页连选自动翻到新页后,起点 / 终点不在本页的那端不再显示手柄(此前会额外画一个贴页界的假手柄),本页只保留仍在页内的拖动端手柄。
  • 阻止新增与已有书签部分重叠的书签:所选文字若与已有书签区间相交(选中了部分已书签文字、或选区横跨已书签与未书签文字),「书签」操作不再静默叠加重叠下划线,而是提示并保留原书签;仅当再次选中与书签完全相同的文字时才移除该书签。

Changed

  • 书签页面优化:使用卡片分隔开不同章节的书签。

v0.1.2

Choose a tag to compare

@github-actions github-actions released this 05 Sep 08:43

Added

  • 书源「网页登录」:Android 端在应用内 WebView 浮层登录后自动捕获站点 Cookie(含 httpOnly)并按书源持久化(重启自动注入,不随书源 JSON 导出);书源 JS 引擎开放 webview.login(url) / webview.isSupported() 宿主 API,书源编辑页提供「网页登录 / 清空登录 Cookie」入口。桌面/iOS 不支持时返回 ok:false 且不抛错。
  • 导入本地同名文件时可选择重新导入(替换书架中同名书,尝试继承阅读进度/分组/书签)或作为新书加入;重新导入前预演书签继承,部分书签无法继承时提示并可放弃重新导入。WebDAV 长按重新导入同样增加该提示与放弃选项。
  • 书籍详情页。
  • 目录(TOC)显示章节下载状态。
  • 阅读设置里新增「重新加载本章」(仅在线书显示该入口)。
  • 在线书获取失败后提示用户并提供重新加载入口。
  • 替换文本功能,支持正则。

Fixed

  • webdav中直接点击阅读,返回后,失去状态数据的问题。
  • tts跟读后翻页异常问题。
  • 自动跟读跨章定位异常的问题。
  • 朗读已进入下一章时点击「返回跟读」无法跳回朗读句的问题(未带上朗读所在章节索引)。
  • 书签按钮异常高亮的问题。

Changed

  • TTS失败后跳过,连续失败多次(5次)后停止。
  • 封面中,使用书籍全名,而不是首字。
  • 封面可以在epub中取。
  • 当章节内容过多时,阅读器显示正在加载,而不是卡页面。
  • 阅读器设置继承设置页面的某些设置项。
  • 当书籍有至少一个书签时,不填充svg图标。
  • 跨页面选取支持(分页模式)。
  • 书签跨段落支持。
  • 将书本来源筛选与分组合并,并可以在设置调整。
  • 设置页面中听书缓存移入到数据中。
  • 分组配置在书架页面长按分组标签即可进入。

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 04 Sep 06:14

Added

  • 本地书导入
  • 添加tts听书功能
  • 搜索全书,书架书籍功能
  • webdav支持
  • 书源