# Hanakura
**Repository Path**: licyk/Hanakura
## Basic Information
- **Project Name**: Hanakura
- **Description**: A local-first CLI and web UI for downloading, organizing, detecting, and managing Stable Diffusion models.
- **Primary Language**: Python
- **License**: GPL-3.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-10-02
- **Last Updated**: 2026-10-12
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
下载和管理 Stable Diffusion 模型,可以使用命令行,也可以使用网页界面。
- **模型下载:** 浏览 Civitai、OpenModelDB 和 GitHub Releases,下载支持断点续传、SHA256
校验、预览图和元数据附属文件。
- **抱脸 / 魔搭:** 从 Hugging Face(或 hf-mirror 等镜像站)和 ModelScope 下载单个文件或整个仓库。
- **链接下载:** 粘贴任意下载地址并选择保存的文件夹,可选填文件名和用于校验的 SHA256。
- **本地模型:** 把 Hanakura 指向 ComfyUI 或 Stable Diffusion WebUI 的模型文件夹。它根据
safetensors 文件头识别每个模型的类型和基础模型(从不反序列化 pickle),显示预览图,并在导入、
移动、重命名和删除模型时一并处理预览图和附属文件。把文件拖进浏览器即可上传。
设计、约定和已知缺陷见 [AGENTS.md](AGENTS.md)。
## 安装
从 PyPI 安装:
```bash
python -m pip install hanakura
hanakura --help
```
需要 Python 3.10 或更新版本。网页界面已打包在安装包内,用户不需要 Node。
支持 Pydantic v1 和 v2。如需在已有环境中保留 Pydantic v1,请使用 Python 3.10–3.13 和兼容的
FastAPI:
```bash
python -m pip install hanakura "pydantic<2" "fastapi<0.126"
```
Python 3.14 及更新版本需要 Pydantic v2。开发检查和仓库中提交的网页 API 类型均使用 Pydantic v2
生成;CI 还会单独测试 Pydantic v1。
## 命令行
```text
hanakura
├── webui 启动服务器并打开网页界面
├── version | env
├── config show | get | set | path
├── source list
├── auth status | use | connect | disconnect
├── search --source --kind --base-model --sort --limit
├── info
├── download
│ ├── model --version --file --root --dir --to
│ ├── url --to --sha256 --name
│ ├── hf --revision --include --exclude --to
│ └── modelscope --revision --include --exclude --to
└── library
├── root list | add | remove
├── list [path] --root --recursive --kind
├── info --hash
├── identify
├── scan --root
├── import --root --to --move --rename
├── move
├── rename
└── delete --permanent --yes
```
所有列表类命令都接受 `--json`,输出与 API 返回相同的记录。`--debug` 在每一级命令上都可用。
出错时按错误类型返回退出码:2 未找到,3 冲突,4 路径或输入无效,5 需要令牌,6 上游失败。
示例:
```bash
hanakura library root add ~/ComfyUI/models --layout comfyui
hanakura library list ~/ComfyUI/models/loras --recursive --kind lora
hanakura search "detail tweaker" --kind lora --base-model "SDXL 1.0"
hanakura download model civitai 122359 # 下载到该布局的 LoRA 文件夹
hanakura download hf stabilityai/sdxl-turbo --include "*.safetensors" --to ./sdxl-turbo
hanakura config set sources.civitai.token
hanakura webui --port 7865
```
HTTP 下载过程中按 Ctrl+C 会暂停下载并保留 `.part` 文件,再次运行同一条命令即可继续。Hub
下载无法暂停(两个库都不支持续传),因此 Ctrl+C 会取消下载。
## 嵌入到其他应用
```python
from hanakura import HanakuraServer, ModelRoot
hub = HanakuraServer(
data_dir="./hub-data", # 数据库和缓存
settings_path="./my-app/hanakura.toml", # 设置文件,可放在任意位置
model_roots=[ModelRoot("/srv/models", layout="comfyui", name="Models")],
lock_model_roots=True, # 用户不能添加、修改或移除文件夹
port=0, # 任意空闲端口;填数字则使用该端口
api_prefix="/tools/hanakura", # 避免与宿主自己的路由冲突
)
url = hub.start() # 开始监听后立即返回,例如 http://127.0.0.1:54123/tools/hanakura
...
hub.stop()
```
| 选项 | 作用 |
| --- | --- |
| `data_dir` | 数据库、缓存以及(默认情况下)设置文件的存放位置 |
| `settings_path` | 单独指定设置文件,与 `data_dir` 分开 |
| `model_roots` | 模型文件夹,每个都有自己的 `layout` 和可选的 `kind` 提示;提供 `id` 后可在下载设置中引用 |
| `lock_model_roots` | 固定模型文件夹:API 拒绝修改,界面隐藏相关操作 |
| `port` | `0` 为任意空闲端口,数字为指定端口(被占用时向上递增,除非设置了 `strict_port`),`None` 使用设置中的端口 |
| `api_prefix` | 把 API、套接字和网页界面放在同一个路径下 |
| `public_base_url` | 受信任的、面向浏览器的界面地址(含代理路径),用于 OAuth 回调和 Cookie;不改变路由 |
| `settings` | 固定其他任意设置,例如 `{"downloads": {"verify_hash": False}}`;被固定的值无法在界面中修改 |
| `host`、`access_token`、`open_browser`、`log_level` | 与命令行相同;非回环地址需要令牌 |
`hub.start()` 不阻塞并返回地址;`hub.run()` 在前台运行;`with HanakuraServer(...) as hub:`
负责启动和停止。`hub.services` 提供模型库、下载和设置供直接调用,`hub.url`、`hub.port` 和
`hub.running` 描述服务器状态。
专用目录可以位于不同的磁盘。例如,宿主可以提供
`ModelRoot("/models/lora", id="host-lora", kind="lora")` 并固定以下设置:
```python
settings={
"downloads": {
"kind_destinations": {
"lora": {"root_id": "host-lora", "rel_dir": ""},
},
},
}
```
`kind` 只是一个备用的文件夹提示:布局中具体的文件夹映射优先于它,文件检测结果仍然独立报告,包括
不一致的情况。在没有明确配置默认值时,它也用于选择默认的下载根目录。宿主提供的根目录 ID
在写入设置时保持不变;未提供的 ID 由解析后的路径确定性地生成。
下载目标的优先级依次为:明确指定的目标或根目录,该模型类型的 `kind_destinations` 条目,
`default_root`,带有匹配类型提示的根目录,第一个根目录。明确指定的相对目录(包括 `""`)
优先于建议值。在选定的根目录内,匹配的类型目标优先于旧的 `kind_folders` 映射和布局默认值。
配置的根目录不存在或相对路径越界时会报错,而不是悄悄下载到别处。通过设置 API 把某个类型目标设为
`null` 可清除它。锁定根目录只固定根目录列表,不会禁用绝对路径的下载目标或服务器端导入。
界面和下载管理器都使用 `LibraryService.suggest_destination(kind, root_id)`;它也可通过
`GET /api/v1/library/destination?kind=lora&root_id=...` 调用(两个参数都可省略)。
获取建议不会创建目录。
如果宿主已有 ASGI 服务器,使用同一个 Python 环境直接挂载应用即可,不需要额外的监听端口或子进程:
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from hanakura.api.app import create_app
from hanakura.core.context import build_services
services = build_services(data_dir=..., settings_overrides=..., roots_locked=True)
hub_app = create_app(services, bound_host="127.0.0.1")
@asynccontextmanager
async def lifespan(app):
try:
async with hub_app.router.lifespan_context(hub_app):
yield
finally:
services.close()
app = FastAPI(lifespan=lifespan)
app.mount("/hanakura", hub_app)
```
宿主必须在提供服务的事件循环上进入和退出子应用的 lifespan;仅仅挂载不会启动下载队列和事件桥。
如果宿主在加载扩展时已经在运行,请在它现有的事件循环上显式进入该上下文,并保持到卸载或关闭为止。
宿主的认证要通过挂载的应用同时作用于 HTTP 和 WebSocket 请求;宿主的路由依赖不会自动保护子应用。
请保留来源检查,并配置预期的主机名(`create_app` 的 `extra_hosts`)。
挂载路径、`root_path` 和 `api_prefix` 都会影响 OAuth 的 Cookie 和重定向路径。使用反向代理时,
`public_base_url="https://example.com/webui/hanakura"` 用于设置受信任的外部界面地址。配置了
`auth.civitai.redirect_uris` 白名单时,它的回调地址也必须在其中,并且必须在服务提供方处注册。
仅凭转发头不能决定回调地址。手动填写的来源令牌无需配置 OAuth 即可继续使用。
命令行同样可以指定前缀:`hanakura webui --api-prefix /tools/hanakura`。
## 设置
设置保存在数据目录的 `settings.toml` 中(`hanakura config path`)。任何设置都可以用环境变量覆盖:
`HANAKURA___`,例如 `HANAKURA_SERVER__PORT=8000` 或
`HANAKURA_NETWORK__PROXY=http://127.0.0.1:7890`。`HANAKURA_DATA_DIR` 用于更改数据目录。
API 从不返回令牌。
服务器默认监听 `127.0.0.1`。要监听其他地址,请先设置 `server.access_token`,之后每个请求都需要它。
### Civitai 认证
有两种方式,可以并存。两者互不替代,也不会自动相互切换。
**个人 API 令牌**(默认方式,大多数人只需要它):
```bash
hanakura config set sources.civitai.token # 或 HANAKURA_SOURCES__CIVITAI__TOKEN
```
**已连接账号**(OAuth,带 PKCE 的授权码流程)。它需要在 Civitai 注册一个 OAuth 应用,因为
client id 标识的是*这一份安装*:
```bash
hanakura config set auth.civitai.oauth_client_id
hanakura webui # 然后在 设置 → Civitai 认证 中点击连接
hanakura auth status # 当前使用哪种凭据,以及它保存在哪里
```
在 Civitai 注册的回调地址必须与服务器实际使用的完全一致,默认是
`http://127.0.0.1:7865/api/v1/auth/civitai/callback`。使用其他端口或主机,或者用 Vite
开发服务器开发界面时,请列出确切的地址:
```bash
hanakura config set auth.civitai.redirect_uris '["http://127.0.0.1:7865/api/v1/auth/civitai/callback", "http://localhost:5173/api/v1/auth/civitai/callback"]'
```
令牌只保存在服务器上:有操作系统凭据存储时存入其中,否则存为数据目录中仅本人可读的文件。它们不会
出现在 `settings.toml` 中,不会发送给浏览器,也不会写入下载记录。访问令牌在过期前约一分钟刷新,
即使多个下载同时请求也只刷新一次,轮换后的一对令牌作为整体保存。`hanakura auth disconnect`
会撤销授权并在本地清除它,API 令牌不受影响。
环境变量优先于以上两者,生效期间设置页会有相应提示。
### 符号链接
模型文件夹中的内容是指向另一块硬盘的符号链接(WebUI 的 LoRA 文件夹常常如此)时,无需额外配置即可
使用:默认跟随链接,因此链接的文件夹会被列出、可以打开并保持你浏览时的路径,也可以作为下载目标。
此时删除、移动和重命名会作用于文件的真实位置。
如果你希望界面无法触及模型文件夹之外的任何内容,可以关闭它:
```bash
hanakura config set library.follow_symlinks false
```
它也在「设置」的“内容”和“本地模型”部分中。关闭后,链接指向根目录之外的内容会直接从列表中隐藏,
而不是先显示出来再在打开时拒绝。无论哪种模式,包含 `..` 的路径、绝对路径和保留名称都会被拒绝,
通过链接环路重复到达的文件夹只遍历一次。
## Hanakura 在模型旁写入的文件
| 文件 | 用途 |
| --- | --- |
| `.hanakura.json` | 来源元数据:模型和版本 ID、基础模型、触发词、SHA256 |
| `.preview.` | 预览图,WebUI 自身的查找规则可以找到它 |
| `.json` | 仅在开启 `downloads.write_webui_metadata` 且文件不存在时写入:WebUI 的用户元数据 |
## 开发
```bash
pip install -e ".[dev]"
python scripts/dev.py # 列出开发任务
python scripts/dev.py dev # 同时启动 API 服务器和网页界面,支持热重载
python scripts/dev.py check # CI 运行的内容:lint、ty、测试、生成的类型
python scripts/dev.py typecheck-py # Python 类型检查;默认以 Python 3.10 为目标
python scripts/dev.py test # pytest、vitest 和 vue-tsc
python scripts/dev.py format # ruff 修复和格式化
python scripts/dev.py typegen # 根据 OpenAPI 模式重新生成 src/api/schema.d.ts
```
开发界面时使用 `dev`:它同时启动 API 服务器和 Vite 开发服务器,在每行输出前标注 `api` 或
`web`,按 Ctrl+C 时两者一起停止。打开它输出的地址(默认 http://localhost:5173);Vite 会把
`/api` 和 `/ws` 代理到 API 服务器,因此一个地址即可访问两者,界面支持热重载。
API 服务器重启期间,页面的套接字会自动重连,代理只提示一次:
```text
[api proxy] http://127.0.0.1:7865 is not answering (ECONNREFUSED). Start it with: hanakura webui --no-open
```
服务器恢复后数据会自动重新出现,不需要刷新页面。
```bash
python scripts/dev.py dev --api-port 8000 --web-port 3000 --data-dir ./dev-data
python scripts/dev.py web-dev # 只启动界面,连接你自己启动的 API 服务器
hanakura webui --no-open # 只启动 API 服务器
```
这些任务是一个小型 Python 脚本而不是 Makefile,因此在 Windows 上的用法完全相同。每个任务都会输出
它运行的命令,方便直接调用底层工具。
网页界面使用 [bun](https://bun.sh)。分层:`hanakura/core` 包含全部逻辑,不导入任何 Web 或 CLI
框架(有测试保证);`hanakura/api`(FastAPI)和 `hanakura/cli`(Typer)是它之上的薄封装。
检测规则是 `hanakura/core/detection/rules_data/` 中的 JSON 文件。从真实文件添加测试夹具:
`python scripts/extract_header_fixture.py `。
## 构建
```bash
python scripts/build_wheel.py # 先用 bun 构建网页界面,再把 wheel 和 sdist 输出到 dist/
python scripts/build_wheel.py --ci # 用于 CI:跳过类型检查,保留已构建的网页界面
python scripts/build_wheel.py --outdir out --keep-web-dist
python scripts/dev.py wheel # 同上,通过任务脚本运行
```
直接运行 `python -m build` 得到的 wheel 不含网页界面,因为 setuptools 只打包运行时已存在的文件;
此时服务器会记录一条警告,只提供 API。