f2-hap:把短视频无水印下载器搬上 HarmonyOS NEXT 的纯 ArkTS 原生实践

项目背景:从 macOS 到鸿蒙 NEXT

f2-hap 是一个抖音 / X(Twitter) / 微博无水印视频与图集下载器的 HarmonyOS NEXT 原生应用,由 macOS 版 F2 Downloader 移植而来,采用 Apache-2.0 协议开源。

它最有意思的地方在于移植方式:没有 Python 运行时,没有第三方解析 API。桌面版是「Swift 进程 + Python 常驻服务 + 文件队列」三段式架构,跨进程通信还曾导致「任务一直停在等待中」的诡异 Bug(Python 子进程退出后 daemon 线程被杀)。而 HAP 版把全部签名算法与解析逻辑都用 ArkTS 在设备本机重写了一遍,联网只跟平台自家接口打交道——没有中间商赚差价,也就没有中间商挂掉。

能做什么

平台 视频 图集 登录要求 说明
抖音 Douyin ✅ ✅ 需 Cookie 本机 a_bogus 签名(SM3 + 自定义 RC4),走 aweme/v1/web/aweme/detail
X (Twitter) ✅ ✅ 免登录 主路线 cdn.syndication.twimg.com;兜底 guest_token + GraphQL
微博 Weibo ✅ ✅ 需 Cookie weibo.com/ajax/statuses/show,自动下潜转发原文
TikTok ⚠️ ⚠️ — 需 XBogus 签名 + 设备注册,当前版本未移植,成功率极低

除了解析下载,工程还把「用起来顺手」这件事做得很细:

  • 系统分享接入:在抖音/微博里点「分享 → F2 HAP」直接建任务,不用手动复制链接
  • 剪贴板自动识别:打开应用即读剪贴板,识别到链接自动填入(动态申请 READ_PASTEBOARD 权限)
  • 短链展开:v.douyin.com / t.cn / t.co 自动跟随跳转,展开后重新判定平台
  • 多档码率择优:视频取 video/mp4 中 bitrate 最高档,跳过 m3u8;图片直接取原图
  • 存入系统相册:走 showAssetsCreationDialog 弹窗授权,不申请受限权限,无需 ACL 审批
  • 断点式落盘:流式写 .part 临时文件,完成后 rename,避免半成品混入
  • 并发闸门:1~5 并发可调,任务队列常驻同一 ArkTS 运行时
  • 实时日志:分级过滤、一键复制,解析失败能直接看到是哪一步断的

架构:单运行时,没有进程边界

整个应用跑在同一个 ArkTS 运行时里,数据流非常清晰:

1
2
3
4
5
6
7
8
9
UI (ArkUI)  ──订阅──▶  TaskManager(内存队列 + 并发闸门)
│
ResolverRegistry ──▶ Douyin / Twitter / Weibo / TikTok Resolver
│ │
│ HttpClient(UA + Cookie + 重试)
▼
Downloader(requestInStream 流式落盘)
▼
GallerySaver(showAssetsCreationDialog)

工程结构也按这条链路组织:

1
2
3
4
5
6
7
8
9
10
11
12
13
f2-hap/
├── entry/src/main/ets/
│ ├── core/
│ │ ├── crypto/ # SM3.ets、ABogus.ets —— 抖音签名算法本机实现
│ │ ├── model/Models.ets # DownloadTask / MediaItem / ResolveResult
│ │ ├── net/HttpClient.ets # 统一 GET/POST、重试、UA、Cookie 注入
│ │ ├── platform/ # 四个 Resolver + PlatformDetector + ResolverRegistry
│ │ ├── download/ # Downloader(流式)、GallerySaver、TaskManager
│ │ ├── store/ConfigStore # preferences 持久化
│ │ └── util/Logger.ets # 环形缓冲 + 订阅推送
│ ├── entryability/ # EntryAbility:初始化 + 接收系统分享
│ └── views/ # 下载/任务/日志/设置 四 Tab
└── scripts/dev-build.sh # 一键构建 + 签名注入 + 安装

对独立开发者来说,这个架构有个很实际的好处:没有进程边界,任务不会凭空停住。代价则是——签名算法必须自己在 ArkTS 里重写一遍。

技术亮点:在 ArkTS 里重写抖音签名

这可能是整个项目含金量最高的部分。抖音 Web 接口的 a_bogus 参数依赖 SM3 国密哈希 + 自定义 RC4 的组合签名,通常这类算法只在 Python/JavaScript 社区流传(配合 execjs 或者 Node 子进程调用)。f2-hap 直接用 ArkTS 原生实现了 SM3.ets 和 ABogus.ets,签名完全在设备本机完成。

X(Twitter) 的解析则展示了「免登录双路」的设计思路:主路线走 cdn.syndication.twimg.com 的公开聚合接口,失败时兜底 guest_token + GraphQL,全程不需要用户登录。

隐私设计:能不申请的权限都不申请

鸿蒙的权限体系比传统 Android 严格得多,f2-hap 的应对策略很值得借鉴:

  • Cookie 只存在应用沙箱的 preferences 里,不上传、不出设备
  • 除目标平台自家域名外,不连任何服务器;没有统计、没有埋点
  • 下载文件落在沙箱 filesDir/downloads,存相册走 showAssetsCreationDialog 系统弹窗逐次确认,而不是申请受限的相册写权限
  • 剪贴板权限在启动时动态申请,并在 module.json5 里写明 reason

构建踩坑实录:DevEco 签名的那些暗坑

项目的 docs/BUILD.md 简直是一份鸿蒙构建踩坑百科,几个典型的坑:

1. 调试 profile 与包名强绑定。 调试 .p7b 里写死了 bundle-name,与 app.json5 不一致时 SignHap 阶段直接报 00303074。本工程包名 cn.lancenas.f2hap,必须在 DevEco 里为本工程重新自动签名。

2. products 必须引用 signingConfig。 即使 signingConfigs 配好了证书和口令,如果 products[].default 少了 "signingConfig": "default" 这一行,hvigor 会静默产出未签名包(部署报 9568320 no signature file)——不报错、不警告,这个坑最阴。

3. 环境变量指向。 DEVECO_SDK_HOME 必须指向含 default/ 的父级目录(hvigor 会自己拼路径);JAVA_HOME 必须指向 DevEco 自带 JBR(JDK 21),否则打包阶段报 00308018,真实原因藏在日志深处是 Unable to locate a Java Runtime。

仓库自带的 scripts/dev-build.sh 把这些全封装了:自动探测 JDK/SDK、从 ~/.ohos/config 读调试证书、校验 p7b 绑定包名、trap 无条件还原 build-profile.json5 防止签名口令入库。一条命令构建安装:

1
2
3
git clone https://github.com/Lancenas/f2-hap.git
cd f2-hap
bash scripts/dev-build.sh debug install

ArkTS 严格模式速查:写给被编译器毒打的开发者

从 TypeScript 转 ArkTS 的人都会经历一段「为什么这也不行」的时期,BUILD.md 里整理的速查表相当实用:

限制 禁止 替代
arkts-no-indexed-signatures interface Q { [k: string]: T } type Q = Record<string, T>
arkts-identifiers-as-prop-names { 'Content-Type': v } 先声明常量再 obj[KEY] = v
arkts-no-untyped-obj-literals 字段不全的对象字面量 class 构造函数 / 工厂函数
arkts-no-destruct-decls const [a, b] = s.split('_') 用下标
arkts-no-structural-typing 结构相同即可赋值 显式构造目标类型
arkts-no-is x is T 类型谓词 返回 boolean,调用方自行收窄

连 String.replace 的回调重载都被禁了,得手写字符扫描。可以说这个项目本身就是一个「ArkTS 严格模式大型实战场」。

总结

f2-hap 的价值不只是一个能用的无水印下载器,更是一份完整的 HarmonyOS NEXT 原生开发参考实现:

  1. 纯 ArkTS 解析引擎:国密签名算法本机化,摆脱对 Python/Node 子进程的依赖
  2. 单运行时架构:用 TaskManager 内存队列替代跨进程文件队列,规避了桌面版的经典坑
  3. 合规的权限策略:能用弹窗授权的绝不申请受限权限
  4. 可复制的工程化经验:一键构建脚本、签名防泄漏、ArkTS 严格模式避坑表

如果你正在做鸿蒙原生应用(尤其是需要网络请求、文件下载、系统集成类的),这个仓库的 core/ 目录和 docs/ 目录都值得逐文件细读。

项目地址:

免责声明:该项目仅供个人学习与备份自己可访问的公开内容使用,请遵守各平台服务条款与著作权法。