Han1meViewer Documentation

Kotlin repository by @misaka10032w. Desktop release binaries & installers with 1.3k stars.

1,258 stars Kotlin Kotlin 1 file · ~2,962 tokens
RAW Doc

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 许可证要求:

  • 保留原版权声明。
  • 包含许可证文件副本。
  • 说明修改内容。

原始仓库:YenalyLiew/Han1meViewer

许可证文件: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。

管理端使用步骤:

  1. 在 HanimeAnnouncementManager.html 中填写 firebaseConfig。
  2. 在 ADMIN_EMAIL 和 ADMIN_PASSWORD 中填入可登录 Firebase Auth 的管理员账号。
  3. 确保 Realtime Database 规则允许该管理员账号读写 announcements 节点。
  4. 如需批量授权管理员,先安装 firebase-admin,准备 serviceAccountKey.json,再运行 PermitAdmin.py 并填写目标用户 UID。
  5. 打开管理页后即可新增、编辑、启用、停用或删除公告。

公告字段说明:

  • 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、视频和通用工具方法。

🔁 整体数据流:

text
Compose Screen -> ViewModel -> NetworkRepo / DatabaseRepo -> Retrofit + Jsoup / Room -> StateFlow -> Compose Screen

🎞️ 视频页数据流:

text
VideoRoute -> VideoViewModel -> NetworkRepo.getHanimeVideo -> Parser -> HanimeVideo -> VideoScreen / Player / CommentScreen

📥 下载数据流:

text
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.properties
  • gradle/libs.versions.toml
  • app/build.gradle.kts
  • buildSrc/src/main/java/Config.kt

🚀 本地运行

  1. 克隆项目:

    bash
    git clone https://github.com/misaka10032w/Han1meViewer.git
  2. 使用 Android Studio 打开项目根目录。

  3. 等待 Gradle Sync 完成。

  4. 选择 debug 变体运行到设备或模拟器。

💻 常用命令:

bash
./gradlew :app:compileDebugKotlin
./gradlew :app:assembleDebug

🪟 Windows PowerShell:

powershell
.\gradlew.bat :app:compileDebugKotlin
.\gradlew.bat :app:assembleDebug

🔏 Release 构建需要本地或 CI 提供签名相关环境变量:

  • KEYSTORE_PASSWORD
  • KEY_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 时应确保数据源唯一。

🗺️ 目录速查

text
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帧集,暂时不考虑服务器在线共享之类的(太危险)。

共享关键库位置

须知

  1. 中字候补(生肉,字幕未上传)时不要上传,因为出字幕后可能会与未出字幕前有一定的时间差,导致时间不准。
  2. 每一个关键H帧的prompt尽可能短,因为太长会影响观看。
  3. 两个关键H帧必须间隔 5s 及以上!

怎么共享关键H帧?

注意:共享关键H帧是无法在程序内部修改的,只可读。

Fork & Clone

先把我项目 Fork 了,然后从你项目里 Clone 到本地。

fork 仓库步骤:

  1. 登录GitHub账号,进入要fork的仓库页面。
  2. 点击右上角的“Fork”按钮。
  3. 等待一段时间后,就可以在个人的GitHub仓库中看到已经fork过来的仓库。

clone 仓库步骤:

  1. 在要clone的仓库页面上找到“clone with HTTPS”或“clone with SSH”按钮,并复制HTTPS或SSH地址。
  2. 在本地打开终端(或命令行界面),进入要存放仓库的文件夹。
  3. 输入命令 git clone <复制的仓库地址>,其中<复制的仓库地址>替换为刚才复制的地址。
  4. 等待一段时间后,就可以在本地文件夹中看到已经克隆过来的仓库。

以上步骤完成后,就可以在本地使用这个仓库了。

创建文件

在该 md 文件所处的文件夹下创建一个 JSON 文件,命名为 <影片代号>.json。
该影片代号从哪里来呢?举个例子,你所看的番,网址是https://hanime1.me/watch?v=37453
,它的代号就是v=后面的37453。如果你想共享该影片的关键H帧,你需要创建名为37453.json的文件。

了解并编写 JSON 结构

以代号为84711的影片为例(内容我乱填的),就按这个格式写:

json
{
  "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 到本地仓库:

  1. 在本地文件夹中找到要修改的代码文件。
  2. 对代码进行修改、添加或删除。
  3. 使用命令 git add <文件名> 将修改后的文件添加到暂存区,也可以使用 git add . 将所有修改后的文件添加到暂存区。
  4. 使用命令 git commit -m "<提交信息>" 提交暂存区的文件到本地仓库,其中 <提交信息> 是本次提交的说明。

push 到远程仓库:

  1. 使用命令 git push origin <分支名> 将当前分支推送到远程仓库,其中 <分支名>
    是要推送的分支名称。例如,如果要将当前分支推送到远程主机的 master
    分支,可以输入 git push origin master。

以上步骤完成后,就可以将代码从本地仓库推送到远程仓库了。

注意,提交信息的格式为:[HKeyframe] 添加/修改/删除 <影片名称>

简繁都行,英文也行。

例如:[HKeyframe] 添加 初恋时间系列

Pull Request

从我本体的 Pull Request 里提交合并请求,如果没什么大问题就合并了。

以下是Pull Request的步骤:

  1. 确认分支:首先,确保你已经在本地仓库中创建了一个新的分支,并且在该分支上进行了修改。
  2. 创建远程分支:将本地分支推送到远程仓库中,可以使用 git push -u origin <分支名>
    命令,其中 <分支名> 是当前分支的名称。
  3. 打开 GitHub 页面:在浏览器中打开目标仓库的页面,并切换到要合并的分支。
  4. 创建 Pull Request:在页面上找到 “New Pull Request” 按钮,点击后会出现一个新的页面,其中包含了你推送的分支和目标仓库的信息。
  5. 填写 Pull Request 信息:在新页面中,可以填写一些关于 Pull Request
    的信息,例如标题、描述、标签等。这些信息可以帮助其他人更好地理解你提交的代码修改。
  6. 提交 Pull Request:最后,点击 “Create Pull Request” 按钮提交 Pull Request。
  7. 等待审核:一旦提交了 Pull Request,等我审核你的修改。如果审核通过,你的修改将合并到目标仓库中。

--- METRICS ---