Skip to content

Releases: BaiFeng940/inkscroll

inkScroll 0.3.6

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 19 Oct 11:43

inkScroll 0.3.6

针对「网盘库里文件多了之后加载很慢」做了一轮实测优化:大库导入快约 3.8 倍。

瓶颈在哪(逐项量出来的)

# 问题 代价
1 扫描子目录串行(一个目录一次 PROPFIND) 100 个文件夹光等就几十秒
2 每本书都新建连接(TCP + TLS 握手) 100 本 = 100 次握手
3 每本嗅探 16 张图的尺寸 每本多十几倍往返
4 书架写入是 O(n²)(每本重写整个书架 JSON) 越到后面越卡
5 启动时给整个书架生成封面 每本下载一整张原图
6 重复导入产生重复的书 书架越用越乱

改了什么

  1. 扫描并行 5 路,结果按原始下标放回,目录自然序不乱
  2. 整批复用同一个连接,最后统一关闭
  3. 嗅探样本 16 → 4:同一话里的图几乎总是同尺寸,中位数兜底足够
  4. 批量写入书架:一次读一次写;导入期间只攒元数据,整批结束才刷新 UI
  5. 封面按需生成:书架卡片可见时才请求;启动时只补本地库
  6. 按 (连接, 目录) 去重,重复添加会明确提示「都已在库里」
  7. HTTP 连接池固定 maxConnectionsPerHost=6,长连接复用

实测数据(A/B 基准,tool/bench_scan.dart)

12 本 × 20 页,用模拟服务器注入固定 RTT:

60ms RTT(较好的隧道)

阶段 优化前 优化后 加速
扫描 940 ms 252 ms 3.73x
单本嗅探 302 ms 77 ms 3.92x
整库 4.6 s 1.2 s 3.88x

150ms RTT(较差的公网)

阶段 优化前 优化后 加速
扫描 2053 ms 517 ms 3.97x
单本嗅探 645 ms 170 ms 3.79x
整库 9.8 s 2.6 s 3.83x

收益与 RTT 成正比 —— 符合「消除往返次数」的预期:网络越慢,提升越大。

顺带修掉的两个真 bug

  • upsertBooks 会覆盖错书:最初「边遍历边 insert(0)」,头部插入让已记录的下标整体错位,按旧下标更新会写到别的书上 —— 书架出现重复项且书名错乱。这个问题是新加的单元测试抓出来的。
  • 测试服务器是串行的:await for 里直接 await 处理请求,用它跑基准会得出「并行没有用」的错误结论(实测只快 1.12x)。改为每请求独立 task 后才测出真实差距。

下载

架构 适用机型
inkscroll-0.3.6-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.3.6-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.3.6-x86_64.apk 模拟器

analyze 0 issue,测试 73 全通过;已在 Android 13 真机(V2073A)验证导入、封面、去重。

inkScroll 0.3.5

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 19 Oct 11:22

inkScroll 0.3.5

三件事:封面改用真实首图、文案去 NAS 化并泛化到各类 WebDAV 网盘、远程访问指引重写。

1. 封面 = 第一张图片

之前在线库的书没有封面,只显示一个「书名首字 + 配色」的占位块。
现在导入时会从第一张图片生成缩略图并落盘,列表直接读本地文件。

三个关键设计:

  • 导入时生成,不在列表里实时拉 —— 在线库的书首页在网盘上,
    等列表滚到才去拉会变成一屏 6 个并发请求,每次冷启动都要重来
  • 批量导入期间推迟 —— 否则每存一本就插一次网络请求,导入会慢数倍;
    整批结束后在后台顺序补(并发会把网盘打满甚至限流)
  • 内存安全 —— 不先解码原图再缩放,而是把目标宽度交给解码器,
    单页即使是 800×6000 的长图,峰值内存也只有缩略图那么大

启动时也会自动给历史数据补封面。

2. 去掉「NAS」,泛化为通用 WebDAV

之前 现在
在线库(NAS / WebDAV) 在线库(WebDAV 网盘)
连接 NAS 连接 WebDAV
添加 NAS 连接 添加 WebDAV 网盘
还没有添加任何 NAS 还没有添加任何网盘
从 NAS 导入 从网盘导入
本地与 NAS 漫画阅读器 本地与网盘漫画阅读器

支持范围写明为:私有云(飞牛 / 群晖 / 威联通 / TrueNAS)
与商用网盘(坚果云 / Nextcloud / Alist 等)。

⚠️ 需要说清楚:百度网盘、阿里云盘这类网盘本身不提供 WebDAV。
要用它们,得先用 Alist 之类的网关把网盘挂载并转成 WebDAV,
再把 Alist 的 WebDAV 地址填进来。App 这边不限服务商,
只要是标准 WebDAV 就能连。

3. 远程访问指引重写

原来只讲「公网 IP + 路由器端口映射」。但光猫 + 路由器双层 NAT
或运营商 CGNAT 的情况下这条路根本走不通(错误提示还会误导成"地址写错")。

现在改成先判断有没有公网 IP,再分两条路:

  • A. 有公网 IP —— 路由器端口映射 + DDNS
  • B. 没有 —— chmlfrp / frp / Cloudflare Tunnel 隧道穿透,
    并给出三个关键配置:端口类型选 TCP、本地端口填 5006、加密压缩都关

验证(Android 13 真机,V2073A)

项目 结果
安装启动、权限 ✅
界面文案无 NAS 字样 ✅
连接 NAS(WebDAV) ✅ 列出 /111/
添加为库 ✅ 在线库出现「111 / 1 页」
封面 = 首图 ✅ 卡片显示从 NAS 取的真实图片

analyze 0 issue,测试 72 全通过。

下载

架构 适用机型
inkscroll-0.3.5-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.3.5-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.3.5-x86_64.apk 模拟器

Full Changelog: v0.3.4...v0.3.5

inkScroll 0.3.4

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 19 Oct 10:47

inkScroll 0.3.4

让 NAS 功能真正可用的最后一个 bug 修掉了。

症状

0.3.3 修好联网权限后,连接能到达 NAS,但列目录依然失败。

根因:飞牛 fnOS 拒绝带请求体的 PROPFIND

用 curl 逐项对比才发现:

请求 结果
PROPFIND 不带请求体 207 + 完整文件列表 ✅
PROPFIND 带 XML 请求体 400 Bad Request ❌

而"标准写法"正是发 <propfind><prop>...</prop></propfind> ——
也就是说任何标准 WebDAV 客户端在飞牛上都列不出目录。

400 这个错误码极具误导性:看起来像"请求格式错",实际极易被误判成
地址写错、权限不足、端口不对。这个坑绕了很久。

修复

RFC 4918 §9.1 规定:PROPFIND 不带请求体等价于 allprop,是合法的。
所以改成默认发空体,仅当服务器明确回 400/415 时才回退到带体写法,
兼容那些反过来要求必须带体的严格实现。

端到端验证(真机 + 公网隧道,非局域网)

用 chmlfrp 隧道 https://8.162.5.68:64815 连家里的 NAS:

步骤 结果
列根目录 207,正确列出 /111/
图片尺寸嗅探(Range) 2559×1439
下载整张 4.7MB 图片 271ms(约 17 MB/s)
App 内导入为在线库 成功,显示「111 / 1 页」
打开阅读 图片正常渲染

至此「不在家也能看 NAS 漫画、且不装额外软件」的完整链路跑通。

飞牛用户配置要点

  1. 别用管理员账号 —— fnOS 的 WebDAV 对管理员返回 401(认证阶段就拒绝),
    请单独建一个普通账号
  2. 该账号需要有可见文件夹,否则根目录返回 404(不是权限错误,是没内容可映射)
  3. 远程访问推荐 chmlfrp / frp 之类的 TCP 隧道:
    • 端口类型选 TCP(不是 HTTP/HTTPS),这样 TLS 端到端保留
    • 本地端口填 5006(WebDAV HTTPS),不是 5666(网页端口)
    • 数据加密/压缩都关掉(TLS 已经加密;JPEG 压不动)
  4. 双层 NAT / 运营商 CGNAT 都不用管,隧道直接绕过

下载

架构 适用机型
inkscroll-0.3.4-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.3.4-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.3.4-x86_64.apk 模拟器

若装过旧版本,可能因签名不同无法覆盖安装,请先卸载。

Full Changelog: v0.3.3...v0.3.4

inkScroll 0.3.3

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 19 Oct 10:19

inkScroll 0.3.3

修掉本项目最严重的 bug:release 包完全无法联网。

症状

从 0.2.0 起,NAS 相关功能在手机上一次都没成功过,一律提示"连接不上服务器"。
看起来像地址写错、网络不通、双层 NAT、端口映射未生效 —— 全都不对。

真正的报错是:

SocketException: Connection failed
(OS Error: Operation not permitted, errno = 1)

errno = 1 是 EPERM —— Android 直接禁止了 App 联网,与网络环境无关。

根因

Flutter 模板只在 src/debug/AndroidManifest.xml 里声明了 INTERNET 权限
(为了热重载),src/main/AndroidManifest.xml 里没有。于是:

包类型 联网
debug ✅ 正常
release ❌ 被系统拒绝

排查时我一直用 flutter build apk --debug 在模拟器上测试,
完美掩盖了这个问题;而用户装的都是 release 包。

修复

  1. src/main/AndroidManifest.xml 补上 INTERNET 与 ACCESS_NETWORK_STATE
  2. 新增 res/xml/network_security_config.xml:Android 9+ 默认禁止明文 HTTP,
    而家用 NAS 的 WebDAV 常见是 http://192.168.x.x:5005。显式放开,
    否则填 http 地址也会被系统拦掉

验证(这次是 release 包)

在真机上用 0.3.3 release 包连接 chmlfrp 隧道 https://8.162.5.68:64815:

修复前 修复后
报错 Operation not permitted (errno=1) HTTP 401
含义 Android 禁止联网 网络通畅,仅账号未通过
进程日志 EPERM 异常 无异常

用假账号测试得到 401 —— 说明隧道、双层 NAT、HTTPS、自签名证书全链路都通。

教训

涉及联网的功能,必须在 release 包上验证。 debug 包自带 INTERNET 权限,
会让这类问题 100% 隐形。已写进代码注释。

下载

架构 适用机型
inkscroll-0.3.3-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.3.3-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.3.3-x86_64.apk 模拟器

⚠️ 如果你装过旧版本,可能因签名不同而无法覆盖安装,请先卸载再装。

Full Changelog: v0.3.2...v0.3.3

inkScroll 0.3.2

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 18 Oct 14:36

inkScroll 0.3.2

这一版针对「不在家时也能看 NAS 漫画,且不装任何额外软件」。

好消息:你不需要装任何中转软件

只要家里的宽带分到了公网 IP,在路由器上做一次端口映射就能直接连 ——
不需要 Tailscale、ZeroTier 之类的东西。

App 里新增了完整指引:设置 → 在线库 → 「不在家时怎么连?(远程访问)」,
4 步讲清楚:

  1. 确认有没有公网 IP(100.64.x.x 开头就是运营商大内网,这条路不通)
  2. 主路由器 → 端口转发:外部端口 → 192.168.x.x:5006
  3. 配一个 DDNS 域名(公网 IP 会变;飞牛 OS 自带 DDNS)
  4. 在 App 里填 https://你的域名:外部端口,和局域网地址并存两条连接

安全提醒(很重要)

把 NAS 暴露到公网有风险,请务必:

  • 在 NAS 上建一个只读的 WebDAV 账号给阅读器
  • 用非标准外部端口
  • 保持 HTTPS

其它改动

  • 协议探测超时 8s → 15s:外网下一次 TLS 握手 + 鉴权往返经常要好几秒,8s 会误判成"连不上"
  • 清掉 UI 文案里残留的字面量 **

关于「用分享链接直接看」

没有做,而且这次确认了做不了:share.fnnas.net/s/<token> 是 FN Connect 的纯前端 SPA,
任何路径都只返回同一个 851 字节的页面,没有取文件的接口;真正的访问要走
/api/v1/fn/con 的隧道中继协议,逆向它等于重写整套 WebSocket 隧道,
随版本变化就会坏。端口映射才是正路。

下载

架构 适用机型
inkscroll-0.3.2-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.3.2-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.3.2-x86_64.apk 模拟器

Full Changelog: v0.3.1...v0.3.2

inkScroll 0.3.1

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 18 Oct 14:11

inkScroll 0.3.1

一个会把好事变坏事的 bug 修掉了:明明能连上的 NAS,却提示"连接不上"。

核心修复:自动识别 http / https

如果你填地址时没写 https://(比如只填 192.168.10.105:5006),
之前会自动补成 http://。而飞牛 OS 的 5006 是纯 HTTPS 端口,
HTTP 请求会被服务器直接断开 —— 于是报"连接不上服务器",
完全误导:地址、网络、账号可能全都没问题。

实测对比:

你填的 实际请求 结果
192.168.10.105:5006 http://... Connection closed → "连接不上"
https://192.168.10.105:5006 https://... 到达服务器 → 401 密码错

修复内容:

  1. 默认协议改成 https(NAS 普遍开 HTTPS)
  2. 新增真的去探测:没写协议就先 https 后 http;写了协议但连不上也会自动换另一种
  3. 判定标准是"服务器有没有回应":拿到 401 / 207 / 404 都算协议对了,
    不会把"密码错"误报成"连不上"
  4. 连接失败时把技术细节显示出来(HTTP 401 / Connection closed),
    不再只给一句"连接不上"
  5. 地址栏提示改为「只填 IP:端口 也可以」

如果你之前被这个 bug 拦住了,升级后再试一次 —— 这次会给出准确的错误原因。

清理

  • 删除 lib/ui/widgets/adaptive_media_box.dart —— 它早就被"只给宽度、高度交给解码器"的方案取代,但 README 里还写着"实现:新增它",属于误导性死代码
  • 清掉 .idea / .iml 等 IDE 垃圾
  • 验证截图从 131 个精简到 6 张关键图

其它

  • CI 里的 APK 文件名不再写死版本号,改为从 pubspec.yaml 读取
  • 测试 67 → 70

下载

架构 适用机型
inkscroll-0.3.1-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.3.1-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.3.1-x86_64.apk 模拟器

注意

  • 想跑满 120Hz,仍需在系统设置里为 inkScroll 手动开启高刷新率
  • release APK 仍使用 Flutter 模板的 debug 签名,仅供测试
  • NAS 密码做的是混淆而非加密;建议单独建一个只读 WebDAV 账号

Full Changelog: v0.3.0...v0.3.1

inkScroll 0.3.0

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 18 Oct 13:45

inkScroll 0.3.0

界面按使用习惯重做了一遍,NAS 入口搬到该在的地方,并且支持一次性导入整个漫画库。

首页改版

  • 删掉顶部标题栏。原来那条 AppBar 里是「inkScroll」文字加一个只会清空搜索框的假搜索按钮,纯占高度。
  • 搜索框移到页面顶部,右侧新增设置按钮。
  • 三个 Tab(全部 / 最近 / 收藏)换成 本地库 / 在线库 —— 按内容来源分比按时间分更符合实际使用。
  • 「导入」按钮只在本地库显示。

NAS 入口搬到设置

「从 NAS 导入」从导入菜单里删掉了:它本质不是「导入一本书」,而是配置一个内容源,放菜单里语义就是错的。

现在在 设置 → 在线库(NAS / WebDAV)→ 添加 WebDAV 库。

一键导入整个库

选一个目录后,会扫描它下面的所有子文件夹,每个含图片的都建成一本书进在线库,不含图片的自动跳过。

实测(5 个子目录):第01话 6 页 / 第02话 6 页 / 第03话 4 页 / 封面 2 页 全部正确,空的文件夹被跳过。

修掉的三个 bug(都是实测才暴露)

  1. 未捕获的异步异常 —— _openLibrary 里没有 try/catch,网络异常变成 unhandled async error,界面毫无反应。
  2. href 编码归一化 —— 有些 WebDAV 服务器直接返回未编码的 href(/dav/第01话 火影/),客户端拼出的 URL 含空格和中文,Uri.parse 失败 → 子目录内容全读不到且没有任何提示。现在 resolveHref 先解码再编码,对已编码/未编码都幂等。
  3. pubspec.yaml 被写成带 BOM,已修正。

测试 65 → 67。

下载

架构 适用机型
inkscroll-0.3.0-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.3.0-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.3.0-x86_64.apk 模拟器

注意

  • 想跑满 120Hz,仍需在系统设置里为 inkScroll 手动开启高刷新率
  • release APK 仍使用 Flutter 模板的 debug 签名,仅供测试
  • NAS 密码做的是混淆而非加密;建议单独建一个只读 WebDAV 账号

Full Changelog: v0.2.0...v0.3.0

inkScroll 0.2.0 — 从 NAS 在线看漫画

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 18 Oct 12:52

inkScroll 0.2.0

本次最大的变化:可以直接连 NAS 在线看漫画了。

新功能 · 从 NAS 导入(WebDAV)

支持 飞牛 OS / 群晖 / 威联通 / Alist / Nextcloud 等所有支持 WebDAV 的服务。

  1. 在 NAS 上开启 WebDAV(飞牛 OS:设置 → 文件服务 → WebDAV,默认端口 5005,路径 /dav)
  2. App 里 导入 → 从 NAS 导入(WebDAV) → 填 http://192.168.1.10:5005/dav + 账号密码
  3. 列出目录 → 进入放图片的文件夹 → 点导入

核心设计:只列目录,不整本下载

时机 行为
导入时 只发一次 PROPFIND,不下载任何图片,第一页立即可见
翻页时 拉当前页 + 后台预取前后各 2 页
缓存 拉下来的页落盘,第二次看就是本地文件,断网也能看已缓存的
嗅探 用 Range 只取前 64KB 算图片尺寸,不必为宽高拉整张图

修掉的两个真 bug(都是实测才发现)

  1. 点开 NAS 导入直接红屏 — _loadRaw() 空存储时返回 const [],而 all() 会对它 sort,抛 Cannot modify an unmodifiable list
  2. 导入时报 PathNotFoundException — WebDAV 导入不往书目录复制文件,写 document.json.tmp 时目录还不存在

其它改进

  • 错误提示从 6 秒延长到 12 秒并可手动关闭(之前一闪而过根本看不到原因)
  • 只新增 http + xml 两个纯 Dart 依赖,不引任何原生插件
  • 新增 tool/webdav_probe.dart:连不上 NAS 时可在电脑上逐项诊断
  • 新增 tool/webdav_test_server.dart:没有 NAS 也能跑通整条链路
  • 测试从 39 个增加到 65 个

下载

架构 适用机型
inkscroll-0.2.0-arm64-v8a.apk 绝大多数现代手机(推荐)
inkscroll-0.2.0-armeabi-v7a.apk 较老的 32 位设备
inkscroll-0.2.0-x86_64.apk 模拟器

注意

  • 想跑满 120Hz,仍需在系统设置里为 inkScroll 手动开启高刷新率(Android 与国产 ROM 的限制)
  • release APK 仍使用 Flutter 模板的 debug 签名,仅供测试,不可用于正式分发
  • NAS 密码做的是 XOR + Base64 混淆而非加密;建议在 NAS 上单独建一个只读 WebDAV 账号

inkScroll 0.1.0

Choose a tag to compare

@BaiFeng940 BaiFeng940 released this 17 Oct 00:21

inkScroll v0.1.0

本地漫画 / 文档阅读器。下载对应架构的 APK 直接安装:

架构 适用
arm64-v8a 绝大多数现代手机(推荐)
armeabi-v7a 较老的 32 位设备
x86_64 模拟器

安装后在系统设置里为 inkScroll 手动开启高刷新率,即可跑满 120Hz
(Android 与国产 ROM 的限制,应用无法绕过)。

详细改动见仓库根目录的 README.md。

Full Changelog: https://github.com/BaiFeng940/inkscroll/commits/v0.1.0