Technical Documentation: misaka10032w/Han1meViewer
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
🚫 请不要在任何公开平台宣传本软件
本软件不接受任何形式的公开宣传。若出现公开宣传、搬运或引流,仓库维护者可能随时归档或隐藏仓库,并删除已编译的发行版。
🌸 Han1meViewer
🔞 R18 警告:未满 18 岁禁止下载和使用。
Han1meViewer 是一个使用 Kotlin 开发的 Android 客户端,用于浏览、搜索、播放和管理 hanime 相关公开视频页面内容。当前项目以 Jetpack Compose、Navigation Compose、ViewModel、StateFlow、Retrofit、Jsoup、Room、WorkManager、Media3/JZVD/MPV 为主要技术栈,围绕视频浏览、详情播放、搜索、用户列表、下载管理、评论、订阅、设置和隐私保护等功能组织。
本应用没有任何官方网站。GitHub Release 与 CI 构建产物是唯一下载及更新渠道。
📜 项目来源与许可
此项目最初 Fork 自 YenalyLiew/Han1meViewer,感谢原作者的贡献。原项目采用 Apache License 2.0。
根据 Apache 2.0 许可证要求:
- 保留原版权声明。
- 包含许可证文件副本。
- 说明修改内容。
许可证文件:LICENSE
⚠️ 免责声明
本应用程序与 https://hanime1.me/ 及其关联方无任何隶属、合作或授权关系。
🔍 数据来源:
- 本应用仅通过常规网络请求和 HTML DOM 解析获取目标网站公开显示的信息。
- 本应用不访问网站后端数据库。
- 本应用不进行注入攻击、绕过授权或获取非公开用户隐私数据。
⚖️ 使用限制:
- 本应用仅用于技术研究、学习交流和移动端体验优化。
- 本应用不提供商业化服务。
- 使用者应自行遵守所在地区法律法规和目标网站规则。
©️ 版权归属:
- 原始视频、图文和相关内容版权归
https://hanime1.me/或原始制作、发行方所有。 - 本应用不存储版权内容,不修改原始内容,不声称拥有内容所有权。
🛡️ 责任豁免:
- 本应用不保证数据完整性、准确性和实时性。
- 使用本应用产生的一切后果由用户自行承担。
- 不得将本应用用于任何非法用途。
若 https://hanime1.me/ 认为本应用行为不当,可通过 GitHub 仓库内置功能联系维护者。
🔄 最后更新日期:2026-05-23
ℹ️ 建议通过官方渠道支持原站内容,并点击广告以支持网站运营者。
💬 参与讨论
Telegram 群组:https://t.me/Han1meViewer
✨ 功能概览
- 🏠 首页浏览:支持首页推荐、最新上传、分类入口、预览页和搜索入口。
- 🎬 视频详情:支持简介、作者、标签、系列视频、相关推荐、评论和子评论。
- ▶️ 视频播放:支持在线播放、本地下载文件播放、画中画、播放内核切换、倍速和画质选择。
- 🔍 搜索系统:支持关键词搜索、高级搜索、历史记录、筛选条件展示和分页加载。
- 👤 用户内容:支持收藏、稍后观看、播放列表、在线历史、订阅作者和创作中心。
- 💬 评论系统:支持评论排序、回复查看、举报和复制分享文本。
- 📥 下载管理:支持后台下载、下载分组(下载时可按系列名自动创建分组)、导入本地文件、自定义下载目录和下载后播放。
- 🔐 账号能力:支持登录、手动录入 cookies、账号资料、头像裁剪、登录状态检测和退出登录。
- 🎨 主题与设置:支持 Material 3 主题、动态颜色、首页布局、播放设置、MPV 设置、下载设置和网络设置。
- 🕵️ 隐私保护:支持应用锁、启动器图标伪装和手动 Cookie 管理。
- 🌐 网络辅助:支持 Cloudflare 处理、备用域名、代理相关设置、CDN/网络检测和更新检查。
- 📅 健康打卡:内置冲了么打卡页面和桌面小组件。
- 🎯 共享关键 H 帧:支持内置关键帧数据读取、设置和共享数据展示。
- 🗞️ 公告系统:支持首页公告展示、公告详情弹窗,并配套
HanimeAnnouncementManagerWebUI维护 Firebase Realtime Database 中的announcements节点。
🗞️ 公告系统使用须知
公告系统分为两部分:
- 用户侧:应用启动后由
MainViewModel.loadAnnouncements()从 Firebase Realtime Database 读取announcements节点,首页会展示有效公告,并可点开公告详情弹窗。 - 维护侧:
HanimeAnnouncementManagerWebUI用于维护公告数据,负责向同一个announcements节点写入、更新或下线公告。
HanimeAnnouncementManagerWebUI 目录包含两个文件:
HanimeAnnouncementManager.html:公告管理网页,用于增删改查公告、切换启用状态。PermitAdmin.py:给 Firebase Auth 用户写入isAdmin自定义声明,用于控制谁能访问管理端。
前置条件:
- 应用必须能够正常访问网络。
- 项目中配置的 Firebase Realtime Database 地址必须可用。
announcements节点下的公告数据结构需要和Announcement模型一致,至少包含title、content、isActive等字段。- 公告内容会按
priority排序,只有isActive = true的公告才会显示。 - 管理端需要启用 Firebase Authentication 和 Realtime Database。
- 管理端需要准备 Firebase Web 配置参数和可登录的管理员邮箱、密码。
- 若使用
PermitAdmin.py,还需要 Firebase Admin SDK 的serviceAccountKey.json。
使用规则:
- 首页公告默认会展示为有效公告列表。
- 用户关闭公告后,会记录
last_dismiss_time,24 小时内不会再次自动弹出。 - 公告详情支持标题、内容、发布时间、图片和按钮文案。
- 若公告配置了链接,内容中可直接展示可点击的 URL。
管理端使用步骤:
- 在
HanimeAnnouncementManager.html中填写firebaseConfig。 - 在
ADMIN_EMAIL和ADMIN_PASSWORD中填入可登录 Firebase Auth 的管理员账号。 - 确保 Realtime Database 规则允许该管理员账号读写
announcements节点。 - 如需批量授权管理员,先安装
firebase-admin,准备serviceAccountKey.json,再运行PermitAdmin.py并填写目标用户 UID。 - 打开管理页后即可新增、编辑、启用、停用或删除公告。
公告字段说明:
title:公告标题。content:公告正文。imageUrl:公告图片地址,可选。positiveText:确认按钮文案,可选。negativeText:取消按钮文案,可选。priority:排序优先级。timestamp:公告时间戳,单位为秒。isActive:是否启用。
维护建议:
- 新公告建议先在 WebUI 中预览,再切换为
isActive = true。 - 过期或不再需要显示的公告应直接下线,而不是删除,以便回溯。
- 重要公告优先级数值应更高或更低需与 WebUI 约定保持一致,避免排序混乱。
📷 截图预览
🧱 当前架构
📦 项目采用多模块 Gradle 结构:
:app:主应用模块,包含界面、导航、业务 ViewModel、网络解析、数据库、下载 Worker、播放器和资源文件。:yenaly_libs:项目内公共基础库,封装通用 Activity、Fragment、ViewModel、偏好设置、工具类和基础组件。buildSrc:构建配置辅助代码,用于版本号、构建类型和提交信息等 Gradle 逻辑。
🗂️ 主应用采用分层组织:
ui.activity:入口 Activity、登录、Cloudflare 处理和手动 Cookie 输入页面。ui.navigation:Navigation Compose 路由定义、主导航、设置导航和安全跳转封装。ui.screen:Compose 页面实现,按首页、搜索、视频、设置、账号、下载等业务域划分。ui.component:可复用 Compose 组件,如视频卡片、弹窗、空/错/加载状态、AppBar、评论卡片等。ui.viewmodel:页面状态和业务动作入口,主要通过StateFlow/SharedFlow对 UI 暴露状态。logic.network:Retrofit Service、OkHttp 拦截器、DNS、CookieJar、更新服务和网络入口。logic.model:页面和网络解析后的领域模型,如HanimeVideo、HanimeInfo、HomePage、Playlists等。logic.dao/logic.entity:Room 数据库、DAO 和实体,用于历史、下载、搜索历史、关键帧、打卡等本地数据。announcement链路:公告数据从 Firebase Realtime Database 的announcements节点读取,首页通过MainViewModel.loadAnnouncements()拉取并展示,管理端使用HanimeAnnouncementManagerWebUI维护公告内容。worker:WorkManager 下载任务和更新任务。util:主题、网络、文件、权限、Cookie、Toast、视频和通用工具方法。
🔁 整体数据流:
Compose Screen -> ViewModel -> NetworkRepo / DatabaseRepo -> Retrofit + Jsoup / Room -> StateFlow -> Compose Screen🎞️ 视频页数据流:
VideoRoute -> VideoViewModel -> NetworkRepo.getHanimeVideo -> Parser -> HanimeVideo -> VideoScreen / Player / CommentScreen📥 下载数据流:
DownloadScreen -> DownloadViewModel -> HanimeDownloadManagerV2 -> WorkManager Worker -> Room -> Flow -> UI🛠️ 技术栈
- Kotlin 2.3.x
- Java 21 toolchain
- Android Gradle Plugin 9.2.x
- Jetpack Compose + Material 3
- Navigation Compose + Kotlin Serialization typed routes
- ViewModel + StateFlow + SharedFlow
- Retrofit 3 + kotlinx.serialization converter
- Jsoup HTML 解析
- Room + KSP
- WorkManager 后台任务
- Coil 3 Compose 图片加载
- Media3 ExoPlayer、JZVD、MPV Android
- Firebase Analytics、Crashlytics、Performance、Remote Config、Realtime Database
- AndroidX Window、Biometric、SplashScreen、Preference
- AboutLibraries 开源许可展示
🧰 构建环境
✅ 推荐环境:
- Android Studio Panda 或更新版本。
- JDK 21。
- Gradle Wrapper 使用仓库内置版本。
- 编译 SDK:
37。 - 最低支持:Android 8.1,API
27。 - 目标 SDK:
37。
📌 关键版本以仓库文件为准:
gradle.propertiesgradle/libs.versions.tomlapp/build.gradle.ktsbuildSrc/src/main/java/Config.kt
🚀 本地运行
克隆项目:
bashgit clone https://github.com/misaka10032w/Han1meViewer.git使用 Android Studio 打开项目根目录。
等待 Gradle Sync 完成。
选择
debug变体运行到设备或模拟器。
💻 常用命令:
./gradlew :app:compileDebugKotlin
./gradlew :app:assembleDebug🪟 Windows PowerShell:
.\gradlew.bat :app:compileDebugKotlin
.\gradlew.bat :app:assembleDebug🔏 Release 构建需要本地或 CI 提供签名相关环境变量:
KEYSTORE_PASSWORDKEY_ALIAS
🔑 GitHub API Token 可通过环境变量或本地文件提供:
- 环境变量:
HA_GITHUB_TOKEN - 本地文件:
app/ha1_github_token.txt
✅ 代码约定
- UI 优先使用 Jetpack Compose,少量历史页面和组件仍保留 XML / ViewBinding / DataBinding。
- 页面状态通过
StateFlow暴露,事件通过SharedFlow或回调传递。 - 网络页面列表追加时应按稳定业务键去重,避免 Compose
Lazy*列表 key 冲突。 LazyColumn、LazyVerticalGrid、LazyRow使用稳定 key 时应确保数据源唯一。
🗺️ 目录速查
Han1meViewer/
├── app/ 主 Android 应用
│ ├── src/main/java/.../logic 网络、解析、模型、数据库、状态
│ ├── src/main/java/.../ui Activity、Compose 页面、导航、组件、ViewModel
│ ├── src/main/java/.../util 通用工具
│ ├── src/main/java/.../worker WorkManager 任务
│ └── src/main/res 资源文件、主题、布局、图标、小组件
├── yenaly_libs/ 项目内公共库
├── buildSrc/ Gradle 构建辅助代码
├── gradle/libs.versions.toml 依赖版本目录
├── README.md 项目说明
└── README_TECH.md 历史技术说明与部分实现笔记🤝 贡献说明
- 提交代码前请先确认可以通过
:app:compileDebugKotlin。 - 修改网络列表、分页或 Compose
Lazy*列表时,请检查重复 key 风险。 - 修改播放、下载、账号、Cookie、Cloudflare、更新逻辑时,请尽量说明验证方式。
- 提交共享关键 H 帧可参考
.github/PULL_REQUEST_TEMPLATE/submit_h_keyframe.md。
🧩 TODO
- 随时有想法随时写。
📄 许可证
本项目继承原始项目的 Apache License 2.0。主要条款包括:
- 允许商用、修改和分发。
- 要求保留版权声明和许可证文件。
- 要求提供修改说明。
- 不提供质量担保。
- 不承担用户使用风险。
完整条款请参阅项目根目录下的 LICENSE 文件。
Overview (`app/src/main/assets/h_keyframes/README.md`)
关键H帧
何为关键H帧?
就是影片到达「顶点」的那一刻。
还在因为不能和影片内「顶点」同步,而不停地记着「顶点」的时刻然后看进度条卡时间同步吗?
现在你可能不需要了。如果你开启了该功能(默认开启),并且在影片内手动标记每一个关键H帧,它会在 5-30s
前在屏幕左上角倒计时提醒(可调整)。在 0-1s 时,会精确到一位小数,方便你更好的控制时间。
此外,还提供了共享关键H帧集。这些关键H帧是内置于软件里的,来源于贡献者在 GitHub
上的补充。目前是随着版本更新(或 CI 实时更新)来更新共享关键H帧集,暂时不考虑服务器在线共享之类的(太危险)。
须知
- 中字候补(生肉,字幕未上传)时不要上传,因为出字幕后可能会与未出字幕前有一定的时间差,导致时间不准。
- 每一个关键H帧的
prompt尽可能短,因为太长会影响观看。 - 两个关键H帧必须间隔 5s 及以上!
怎么共享关键H帧?
注意:共享关键H帧是无法在程序内部修改的,只可读。
Fork & Clone
先把我项目 Fork 了,然后从你项目里 Clone 到本地。
fork 仓库步骤:
- 登录GitHub账号,进入要fork的仓库页面。
- 点击右上角的“Fork”按钮。
- 等待一段时间后,就可以在个人的GitHub仓库中看到已经fork过来的仓库。
clone 仓库步骤:
- 在要clone的仓库页面上找到“clone with HTTPS”或“clone with SSH”按钮,并复制HTTPS或SSH地址。
- 在本地打开终端(或命令行界面),进入要存放仓库的文件夹。
- 输入命令
git clone <复制的仓库地址>,其中<复制的仓库地址>替换为刚才复制的地址。 - 等待一段时间后,就可以在本地文件夹中看到已经克隆过来的仓库。
以上步骤完成后,就可以在本地使用这个仓库了。
创建文件
在该 md 文件所处的文件夹下创建一个 JSON 文件,命名为 <影片代号>.json。
该影片代号从哪里来呢?举个例子,你所看的番,网址是https://hanime1.me/watch?v=37453
,它的代号就是v=后面的37453。如果你想共享该影片的关键H帧,你需要创建名为37453.json的文件。
了解并编写 JSON 结构
以代号为84711的影片为例(内容我乱填的),就按这个格式写:
{
"videoCode": "84711",
"group": "aaa",
"title": "abc",
"episode": 1,
"author": "yenaly",
"keyframes": [
{
"position": 1000000,
"prompt": "abc"
},
{
"position": 2000000,
"prompt": null
}
]
}介绍一下每一个键值是干什么的:
注意-1:
author为null时代表个人本地储存的,不为空才代表是共享的。所以不允许 JSON 文件里出现null的author。注意-2:编写前务必查询当前已上传的关键H帧是否存在。假设有一人已上传 AAA 的第一集,group 名为
aaa,title 为aaa-01,你也要按照他的方式进行修改,不要自行更改 group 名为AAA。如果你觉得你的命名更准确、更优质,也可以选择将其所有当前系列的group名和title名一起修改。
| 键 | 值类型 | 作用 |
|---|---|---|
| videoCode | string (not null) | 影片代号,用于识别影片 |
| group | string (nullable) | 影片系列名称,用于影片分组,为 null 代表无系列 |
| title | string (not null) | 影片标题,看得清晰些 |
| episode | number | 影片集数,如果无系列请填 0 |
| author | string (not null) | 关键H帧作者,也就是你,而不是影片作者! |
| keyframes | array | 关键H帧列表 |
| position | number | 「顶点」位置,怎么获取下面说,两个 position 必须间隔 5s 及以上 |
| prompt | string (nullable) | 「顶点」提示,最好少于五个字,或 null |
怎么获取「顶点」位置?
首先要打开关键H帧功能,然后进入该影片全屏界面,在「顶点」处暂停,长按选单栏的🥵按钮,会弹出一个窗口,例如:
......
当前时刻:1830ms
此时,1830就是你所需要填写的position数值。同时,你也可以在此时保存到你的个人本地关键H帧中。
Commit & Push
将代码提交到本地仓库和推送到远程仓库的步骤如下:
commit 到本地仓库:
- 在本地文件夹中找到要修改的代码文件。
- 对代码进行修改、添加或删除。
- 使用命令
git add <文件名>将修改后的文件添加到暂存区,也可以使用git add .将所有修改后的文件添加到暂存区。 - 使用命令
git commit -m "<提交信息>"提交暂存区的文件到本地仓库,其中<提交信息>是本次提交的说明。
push 到远程仓库:
- 使用命令
git push origin <分支名>将当前分支推送到远程仓库,其中<分支名>
是要推送的分支名称。例如,如果要将当前分支推送到远程主机的 master
分支,可以输入git push origin master。
以上步骤完成后,就可以将代码从本地仓库推送到远程仓库了。
注意,提交信息的格式为:
[HKeyframe] 添加/修改/删除 <影片名称>简繁都行,英文也行。
例如:
[HKeyframe] 添加 初恋时间系列
Pull Request
从我本体的 Pull Request 里提交合并请求,如果没什么大问题就合并了。
以下是Pull Request的步骤:
- 确认分支:首先,确保你已经在本地仓库中创建了一个新的分支,并且在该分支上进行了修改。
- 创建远程分支:将本地分支推送到远程仓库中,可以使用
git push -u origin <分支名>
命令,其中<分支名>是当前分支的名称。 - 打开 GitHub 页面:在浏览器中打开目标仓库的页面,并切换到要合并的分支。
- 创建 Pull Request:在页面上找到 “New Pull Request” 按钮,点击后会出现一个新的页面,其中包含了你推送的分支和目标仓库的信息。
- 填写 Pull Request 信息:在新页面中,可以填写一些关于 Pull Request
的信息,例如标题、描述、标签等。这些信息可以帮助其他人更好地理解你提交的代码修改。 - 提交 Pull Request:最后,点击 “Create Pull Request” 按钮提交 Pull Request。
- 等待审核:一旦提交了 Pull Request,等我审核你的修改。如果审核通过,你的修改将合并到目标仓库中。
--- METRICS ---
- Files Extracted: 2
- Estimated Token Budget: ~2876 tokens
- Canonical Reference: https://codewiki.google/github.com/misaka10032w/Han1meViewer