sd-model-hub 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sd_model_hub-0.1.0/PKG-INFO +295 -0
- sd_model_hub-0.1.0/README.md +256 -0
- sd_model_hub-0.1.0/pyproject.toml +85 -0
- sd_model_hub-0.1.0/sd_model_hub/__init__.py +33 -0
- sd_model_hub-0.1.0/sd_model_hub/__main__.py +6 -0
- sd_model_hub-0.1.0/sd_model_hub/api/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/api/app.py +86 -0
- sd_model_hub-0.1.0/sd_model_hub/api/deps.py +14 -0
- sd_model_hub-0.1.0/sd_model_hub/api/errors.py +41 -0
- sd_model_hub-0.1.0/sd_model_hub/api/openapi.py +29 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/app_info.py +56 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/auth.py +117 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/downloads.py +54 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/hubs.py +37 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/library.py +143 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/settings.py +45 -0
- sd_model_hub-0.1.0/sd_model_hub/api/routers/sources.py +49 -0
- sd_model_hub-0.1.0/sd_model_hub/api/security.py +124 -0
- sd_model_hub-0.1.0/sd_model_hub/api/sockets.py +75 -0
- sd_model_hub-0.1.0/sd_model_hub/api/static.py +49 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/app.py +110 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/commands/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/commands/config.py +47 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/commands/download.py +164 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/commands/library.py +239 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/commands/search.py +142 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/commands/system.py +48 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/commands/webui.py +75 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/factory.py +91 -0
- sd_model_hub-0.1.0/sd_model_hub/cli/output.py +67 -0
- sd_model_hub-0.1.0/sd_model_hub/core/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/core/auth/__init__.py +7 -0
- sd_model_hub-0.1.0/sd_model_hub/core/auth/models.py +54 -0
- sd_model_hub-0.1.0/sd_model_hub/core/auth/oauth_client.py +156 -0
- sd_model_hub-0.1.0/sd_model_hub/core/auth/service.py +282 -0
- sd_model_hub-0.1.0/sd_model_hub/core/auth/store.py +220 -0
- sd_model_hub-0.1.0/sd_model_hub/core/auth/transactions.py +89 -0
- sd_model_hub-0.1.0/sd_model_hub/core/context.py +67 -0
- sd_model_hub-0.1.0/sd_model_hub/core/db/__init__.py +5 -0
- sd_model_hub-0.1.0/sd_model_hub/core/db/database.py +114 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/__init__.py +6 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/header.py +150 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/kinds.py +155 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/models.py +142 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/rules.py +121 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/rules_data/diffusion.json +162 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/rules_data/lora.json +82 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/rules_data/other.json +116 -0
- sd_model_hub-0.1.0/sd_model_hub/core/detection/service.py +241 -0
- sd_model_hub-0.1.0/sd_model_hub/core/downloads/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/core/downloads/http_downloader.py +184 -0
- sd_model_hub-0.1.0/sd_model_hub/core/downloads/hub_runner.py +166 -0
- sd_model_hub-0.1.0/sd_model_hub/core/downloads/job.py +44 -0
- sd_model_hub-0.1.0/sd_model_hub/core/downloads/manager.py +613 -0
- sd_model_hub-0.1.0/sd_model_hub/core/downloads/models.py +84 -0
- sd_model_hub-0.1.0/sd_model_hub/core/errors.py +75 -0
- sd_model_hub-0.1.0/sd_model_hub/core/events/__init__.py +6 -0
- sd_model_hub-0.1.0/sd_model_hub/core/events/bus.py +53 -0
- sd_model_hub-0.1.0/sd_model_hub/core/events/models.py +78 -0
- sd_model_hub-0.1.0/sd_model_hub/core/hubs/__init__.py +6 -0
- sd_model_hub-0.1.0/sd_model_hub/core/hubs/base.py +82 -0
- sd_model_hub-0.1.0/sd_model_hub/core/hubs/huggingface.py +89 -0
- sd_model_hub-0.1.0/sd_model_hub/core/hubs/models.py +61 -0
- sd_model_hub-0.1.0/sd_model_hub/core/hubs/modelscope.py +86 -0
- sd_model_hub-0.1.0/sd_model_hub/core/hubs/registry.py +80 -0
- sd_model_hub-0.1.0/sd_model_hub/core/hubs/worker.py +94 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/__init__.py +5 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/fsops.py +78 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/layouts.py +129 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/models.py +132 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/previews.py +116 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/safety.py +101 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/service.py +611 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/sidecar.py +111 -0
- sd_model_hub-0.1.0/sd_model_hub/core/library/thumbnails.py +36 -0
- sd_model_hub-0.1.0/sd_model_hub/core/net/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/core/net/http.py +41 -0
- sd_model_hub-0.1.0/sd_model_hub/core/net/ports.py +66 -0
- sd_model_hub-0.1.0/sd_model_hub/core/net/runtime_file.py +51 -0
- sd_model_hub-0.1.0/sd_model_hub/core/paths.py +22 -0
- sd_model_hub-0.1.0/sd_model_hub/core/record.py +13 -0
- sd_model_hub-0.1.0/sd_model_hub/core/settings/__init__.py +6 -0
- sd_model_hub-0.1.0/sd_model_hub/core/settings/models.py +150 -0
- sd_model_hub-0.1.0/sd_model_hub/core/settings/service.py +233 -0
- sd_model_hub-0.1.0/sd_model_hub/core/sources/__init__.py +6 -0
- sd_model_hub-0.1.0/sd_model_hub/core/sources/base.py +146 -0
- sd_model_hub-0.1.0/sd_model_hub/core/sources/civitai.py +221 -0
- sd_model_hub-0.1.0/sd_model_hub/core/sources/github_releases.py +114 -0
- sd_model_hub-0.1.0/sd_model_hub/core/sources/models.py +126 -0
- sd_model_hub-0.1.0/sd_model_hub/core/sources/openmodeldb.py +158 -0
- sd_model_hub-0.1.0/sd_model_hub/core/sources/registry.py +150 -0
- sd_model_hub-0.1.0/sd_model_hub/embed.py +292 -0
- sd_model_hub-0.1.0/sd_model_hub/logger.py +24 -0
- sd_model_hub-0.1.0/sd_model_hub/version.py +3 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/__init__.py +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/BrowseView-BsgxJoVw.js +4 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/BrowseView-Cc2rOaSu.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/DestinationPicker-Bhq-e6u1.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/DestinationPicker-CcjOH3hU.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/HubsView-QQtySBy8.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/HubsView-nJ91utuU.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/LibraryView-B5TiV-Ff.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/LibraryView-BqayayWs.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/ModelCard-BkbIdLbR.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/ModelCard-DxBbG1CQ.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/ModelGrid-CxsHdr92.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/ModelGrid-DjjnYTWr.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/SettingsView-BesWiAeg.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/SettingsView-uChTre8h.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/hubs-D20xHqWZ.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/index-CUVnymEA.css +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/index-IusZG4tg.js +650 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/library-euRn7QD7.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/roboto-flex-cyrillic-ext-wght-normal-CVcCQn0y.woff2 +0 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/roboto-flex-cyrillic-wght-normal-BJ-nQEdz.woff2 +0 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/roboto-flex-greek-wght-normal-DeN4rzfy.woff2 +0 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/roboto-flex-latin-ext-wght-normal-gysdOMcS.woff2 +0 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/roboto-flex-latin-wght-normal-n-dP9qr2.woff2 +0 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/roboto-flex-vietnamese-wght-normal-AVFfiTln.woff2 +0 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/assets/sources-N2C54ABB.js +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/favicon.svg +1 -0
- sd_model_hub-0.1.0/sd_model_hub/webui/dist/index.html +28 -0
- sd_model_hub-0.1.0/sd_model_hub.egg-info/PKG-INFO +295 -0
- sd_model_hub-0.1.0/sd_model_hub.egg-info/SOURCES.txt +129 -0
- sd_model_hub-0.1.0/sd_model_hub.egg-info/dependency_links.txt +1 -0
- sd_model_hub-0.1.0/sd_model_hub.egg-info/entry_points.txt +2 -0
- sd_model_hub-0.1.0/sd_model_hub.egg-info/requires.txt +23 -0
- sd_model_hub-0.1.0/sd_model_hub.egg-info/top_level.txt +1 -0
- sd_model_hub-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sd-model-hub
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Download and manage Stable Diffusion models from the command line and a web UI
|
|
5
|
+
Classifier: Development Status :: 3 - Alpha
|
|
6
|
+
Classifier: Environment :: Console
|
|
7
|
+
Classifier: Environment :: Web Environment
|
|
8
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: fastapi
|
|
20
|
+
Requires-Dist: uvicorn
|
|
21
|
+
Requires-Dist: websockets>=12
|
|
22
|
+
Requires-Dist: python-socketio
|
|
23
|
+
Requires-Dist: pydantic>=2
|
|
24
|
+
Requires-Dist: httpx
|
|
25
|
+
Requires-Dist: huggingface_hub
|
|
26
|
+
Requires-Dist: modelscope
|
|
27
|
+
Requires-Dist: typer>=0.26.7
|
|
28
|
+
Requires-Dist: rich
|
|
29
|
+
Requires-Dist: Pillow>=10.0
|
|
30
|
+
Requires-Dist: send2trash
|
|
31
|
+
Requires-Dist: tomli-w
|
|
32
|
+
Requires-Dist: tomli; python_version < "3.11"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: keyring; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff; extra == "dev"
|
|
37
|
+
Requires-Dist: tomli; extra == "dev"
|
|
38
|
+
Requires-Dist: ty; extra == "dev"
|
|
39
|
+
|
|
40
|
+
# SD Model Hub
|
|
41
|
+
|
|
42
|
+
Download and manage Stable Diffusion models, from the command line or a web UI.
|
|
43
|
+
|
|
44
|
+
- **Browse** Civitai, OpenModelDB and GitHub Releases, and download with resume, SHA256
|
|
45
|
+
checks, preview images and metadata sidecars.
|
|
46
|
+
- **Hubs:** download files or whole repositories from Hugging Face (or a mirror such as
|
|
47
|
+
hf-mirror) and ModelScope.
|
|
48
|
+
- **Library:** point SD Model Hub at your ComfyUI or Stable Diffusion WebUI model folders. It
|
|
49
|
+
identifies each model's type and base model from the safetensors header (never unpickling
|
|
50
|
+
anything), shows previews, and imports, moves, renames and deletes models together with
|
|
51
|
+
their preview images and sidecar files. Drag files into the browser to upload them.
|
|
52
|
+
|
|
53
|
+
The design, the conventions and the known gaps are in [AGENTS.md](AGENTS.md).
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install . # from a checkout; see "Building" for the web UI
|
|
59
|
+
sd-model-hub --help
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Python 3.10 or newer. The web UI is bundled into the package; users do not need Node.
|
|
63
|
+
|
|
64
|
+
## Command line
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
sd-model-hub
|
|
68
|
+
├── webui start the server and open the web UI
|
|
69
|
+
├── version | env
|
|
70
|
+
├── config show | get | set | path
|
|
71
|
+
├── source list
|
|
72
|
+
├── auth status | use <manual|oauth> | connect | disconnect
|
|
73
|
+
├── search <query> --source --kind --base-model --sort --limit
|
|
74
|
+
├── info <source> <model-id>
|
|
75
|
+
├── download
|
|
76
|
+
│ ├── model <source> <model-id> --version --file --root --dir --to
|
|
77
|
+
│ ├── url <url> --to --sha256 --name
|
|
78
|
+
│ ├── hf <repo-id> --revision --include --exclude --to
|
|
79
|
+
│ └── modelscope <repo-id> --revision --include --exclude --to
|
|
80
|
+
└── library
|
|
81
|
+
├── root list | add | remove
|
|
82
|
+
├── list [path] --root --recursive --kind
|
|
83
|
+
├── info <path> --hash
|
|
84
|
+
├── identify <path>
|
|
85
|
+
├── scan --root
|
|
86
|
+
├── import <paths...> --root --to --move --rename
|
|
87
|
+
├── move <src...> <dst>
|
|
88
|
+
├── rename <path> <new-name>
|
|
89
|
+
└── delete <paths...> --permanent --yes
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Every listing command accepts `--json`, which prints the same records the API returns.
|
|
93
|
+
`--debug` works at every level. Errors exit with a code per kind: 2 not found, 3 conflict,
|
|
94
|
+
4 invalid path or input, 5 needs a token, 6 upstream failure.
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
sd-model-hub library root add ~/ComfyUI/models --layout comfyui
|
|
100
|
+
sd-model-hub library list ~/ComfyUI/models/loras --recursive --kind lora
|
|
101
|
+
sd-model-hub search "detail tweaker" --kind lora --base-model "SDXL 1.0"
|
|
102
|
+
sd-model-hub download model civitai 122359 # into the layout's LoRA folder
|
|
103
|
+
sd-model-hub download hf stabilityai/sdxl-turbo --include "*.safetensors" --to ./sdxl-turbo
|
|
104
|
+
sd-model-hub config set sources.civitai.token <token>
|
|
105
|
+
sd-model-hub webui --port 7865
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Ctrl+C during an HTTP download pauses it and keeps the `.part` file; running the same command
|
|
109
|
+
again resumes. Hub downloads cannot pause (neither library resumes), so Ctrl+C cancels them.
|
|
110
|
+
|
|
111
|
+
## Embedding in another application
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from sd_model_hub import ModelHubServer, ModelRoot
|
|
115
|
+
|
|
116
|
+
hub = ModelHubServer(
|
|
117
|
+
data_dir="./hub-data", # database and caches
|
|
118
|
+
settings_path="./my-app/model-hub.toml", # the settings file, wherever you want it
|
|
119
|
+
model_roots=[ModelRoot("/srv/models", layout="comfyui", name="Models")],
|
|
120
|
+
lock_model_roots=True, # the user cannot add, change or remove folders
|
|
121
|
+
port=0, # any free port; a number asks for that one
|
|
122
|
+
api_prefix="/tools/model-hub", # keeps our routes clear of yours
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
url = hub.start() # returns as soon as it is listening, e.g. http://127.0.0.1:54123/tools/model-hub
|
|
126
|
+
...
|
|
127
|
+
hub.stop()
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
| Option | Effect |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `data_dir` | Where the database, caches and (by default) the settings file live |
|
|
133
|
+
| `settings_path` | The settings file on its own, apart from `data_dir` |
|
|
134
|
+
| `model_roots` | Folders of models, each with its own `layout`: `comfyui`, `sd-webui` or `custom` |
|
|
135
|
+
| `lock_model_roots` | Fixes them: the API refuses changes and the interface hides those actions |
|
|
136
|
+
| `port` | `0` any free port, a number for that one (moving up unless `strict_port`), `None` for the configured one |
|
|
137
|
+
| `api_prefix` | Serves the API, the socket and the web UI under one path |
|
|
138
|
+
| `settings` | Pins any other setting, e.g. `{"downloads": {"verify_hash": False}}`; pinned values cannot be changed in the UI |
|
|
139
|
+
| `host`, `access_token`, `open_browser`, `log_level` | As for the command line; a non-loopback host requires a token |
|
|
140
|
+
|
|
141
|
+
`hub.start()` is non-blocking and returns the URL; `hub.run()` serves in the foreground;
|
|
142
|
+
`with ModelHubServer(...) as hub:` does both ends. `hub.services` exposes the library, downloads
|
|
143
|
+
and settings for direct use, and `hub.url`, `hub.port` and `hub.running` describe the server.
|
|
144
|
+
|
|
145
|
+
The command line can also take the prefix: `sd-model-hub webui --api-prefix /tools/model-hub`.
|
|
146
|
+
|
|
147
|
+
## Settings
|
|
148
|
+
|
|
149
|
+
Settings live in `settings.toml` in the data directory (`sd-model-hub config path`). Any setting
|
|
150
|
+
can be overridden with an environment variable: `SD_MODEL_HUB_<GROUP>__<FIELD>`, for example
|
|
151
|
+
`SD_MODEL_HUB_SERVER__PORT=8000` or `SD_MODEL_HUB_NETWORK__PROXY=http://127.0.0.1:7890`.
|
|
152
|
+
`SD_MODEL_HUB_DATA_DIR` moves the data directory. Tokens are never returned by the API.
|
|
153
|
+
|
|
154
|
+
The server listens on `127.0.0.1` by default. To listen on another address, set
|
|
155
|
+
`server.access_token` first; every request then needs it.
|
|
156
|
+
|
|
157
|
+
### Civitai authentication
|
|
158
|
+
|
|
159
|
+
Two ways, side by side. Neither replaces the other, and nothing switches between them by itself.
|
|
160
|
+
|
|
161
|
+
**A personal API token** (the default, and all most people need):
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
sd-model-hub config set sources.civitai.token <token> # or SD_MODEL_HUB_SOURCES__CIVITAI__TOKEN
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**A connected account** (OAuth, Authorization Code with PKCE). It needs an OAuth application
|
|
168
|
+
registered with Civitai, because the client id identifies *this installation*:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
sd-model-hub config set auth.civitai.oauth_client_id <client id>
|
|
172
|
+
sd-model-hub webui # then Settings → Civitai authentication → Connect
|
|
173
|
+
sd-model-hub auth status # which credential is in use, and where it is kept
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Register the callback with Civitai exactly as the server uses it, by default
|
|
177
|
+
`http://127.0.0.1:7865/api/v1/auth/civitai/callback`. On another port or host, or when working
|
|
178
|
+
on the UI with the Vite dev server, list the exact URLs:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
sd-model-hub 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"]'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Tokens stay on the server: in the operating system's credential store when there is one, else a
|
|
185
|
+
file in the data directory readable only by you. They are never in `settings.toml`, never sent
|
|
186
|
+
to the browser, and never written into download records. Access tokens are refreshed about a
|
|
187
|
+
minute before they expire, once even if several downloads ask at the same time, and the rotated
|
|
188
|
+
pair is stored as a whole. `sd-model-hub auth disconnect` revokes the authorization and forgets
|
|
189
|
+
it locally, leaving any API token untouched.
|
|
190
|
+
|
|
191
|
+
An environment variable takes precedence over both, and the settings page says so while it does.
|
|
192
|
+
|
|
193
|
+
### Symbolic links
|
|
194
|
+
|
|
195
|
+
A model folder whose contents are symbolic links to another disk — as a WebUI install often has
|
|
196
|
+
for its LoRAs — needs one setting, because links that leave the model folder are refused by
|
|
197
|
+
default:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
sd-model-hub config set library.follow_symlinks true
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
It is also in Settings, under Content & Library. With it on, linked folders open and keep the
|
|
204
|
+
path you navigate with; deleting, moving and renaming then act on the files where they really
|
|
205
|
+
are. Paths containing `..`, absolute paths and reserved names are still refused in either mode,
|
|
206
|
+
and a folder reached twice through a loop of links is walked once.
|
|
207
|
+
|
|
208
|
+
## Files SD Model Hub writes next to a model
|
|
209
|
+
|
|
210
|
+
| File | Purpose |
|
|
211
|
+
| --- | --- |
|
|
212
|
+
| `<name>.sdmodelhub.json` | Source metadata: model and version ids, base model, trigger words, SHA256 |
|
|
213
|
+
| `<name>.preview.<ext>` | Preview image, found by the WebUI's own lookup |
|
|
214
|
+
| `<name>.json` | Only when `downloads.write_webui_metadata` is on, and only if absent: the WebUI's user metadata |
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
pip install -e ".[dev]"
|
|
220
|
+
python scripts/dev.py # list the developer tasks
|
|
221
|
+
python scripts/dev.py dev # API server and web UI together, with hot reload
|
|
222
|
+
python scripts/dev.py check # what CI runs: lint, ty, tests, generated types
|
|
223
|
+
python scripts/dev.py typecheck-py # Python types; targets Python 3.10 by default
|
|
224
|
+
python scripts/dev.py test # pytest, vitest and vue-tsc
|
|
225
|
+
python scripts/dev.py format # ruff fixes and formatting
|
|
226
|
+
python scripts/dev.py typegen # regenerate src/api/schema.d.ts from the OpenAPI schema
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`dev` is the one to use while working on the UI: it starts the API server and the Vite dev
|
|
230
|
+
server together, tags each line of output with `api` or `web`, and stops both on Ctrl+C. Open
|
|
231
|
+
the address it prints (http://localhost:5173 by default); Vite proxies `/api` and `/ws` to the
|
|
232
|
+
API server, so one address serves both and the UI hot-reloads.
|
|
233
|
+
|
|
234
|
+
While the API server is restarting, the page's socket reconnects on its own and the proxy says
|
|
235
|
+
so once:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
[api proxy] http://127.0.0.1:7865 is not answering (ECONNREFUSED). Start it with: sd-model-hub webui --no-open
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Data reappears by itself once the server is back; there is no need to reload the page.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
python scripts/dev.py dev --api-port 8000 --web-port 3000 --data-dir ./dev-data
|
|
245
|
+
python scripts/dev.py web-dev # only the UI, against an API server you started yourself
|
|
246
|
+
sd-model-hub webui --no-open # only the API server
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The tasks are a small Python script rather than a Makefile, so they work the same on Windows.
|
|
250
|
+
Each one prints the command it runs, so the underlying tools stay easy to call directly.
|
|
251
|
+
|
|
252
|
+
The web UI uses [bun](https://bun.sh). Layers: `sd_model_hub/core` holds all logic and imports
|
|
253
|
+
no web or CLI framework (a test enforces it); `sd_model_hub/api` (FastAPI) and `sd_model_hub/cli`
|
|
254
|
+
(Typer) are thin wrappers over it.
|
|
255
|
+
|
|
256
|
+
Detection rules are JSON files in `sd_model_hub/core/detection/rules_data/`. To add a test
|
|
257
|
+
fixture from a real file: `python scripts/extract_header_fixture.py <file> <name>`.
|
|
258
|
+
|
|
259
|
+
## Building
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
python scripts/build_wheel.py # web UI with bun, then the wheel and sdist into dist/
|
|
263
|
+
python scripts/build_wheel.py --ci # for CI: skips the type-check, keeps the built web UI
|
|
264
|
+
python scripts/build_wheel.py --outdir out --keep-web-dist
|
|
265
|
+
python scripts/dev.py wheel # the same, through the task runner
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
A bare `python -m build` produces a wheel without the web UI, because setuptools packages only
|
|
269
|
+
the files that exist when it runs; the server then logs a warning and serves the API only.
|
|
270
|
+
|
|
271
|
+
## Releasing
|
|
272
|
+
|
|
273
|
+
`.github/workflows/release.yml` publishes to PyPI when a push to `main` changes
|
|
274
|
+
`sd_model_hub/version.py`, when a `v*` tag is pushed, or through **Run workflow** in the Actions tab.
|
|
275
|
+
For a version change on `main`:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
python scripts/dev.py check # first, locally
|
|
279
|
+
# bump VERSION in sd_model_hub/version.py, commit
|
|
280
|
+
git push origin main
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The workflow runs the tests and ty checks on Python 3.10 to 3.14, the web UI tests and type-check, and the
|
|
284
|
+
generated-types check; for tag runs it verifies that the tag matches `sd_model_hub/version.py`, then builds
|
|
285
|
+
the wheel **with the web UI in it**, checks the wheel really contains the UI and the detection
|
|
286
|
+
rules, installs it into a clean environment and runs `sd-model-hub version`. Only then does it
|
|
287
|
+
publish. Tag runs also create the GitHub release with the built files attached.
|
|
288
|
+
|
|
289
|
+
Publishing runs `python -m twine upload` directly on the runner, without a Docker action.
|
|
290
|
+
Existing files are skipped so retrying a release does not upload them again.
|
|
291
|
+
|
|
292
|
+
Set-up, once: create a PyPI API token and save it as the GitHub Actions secret `TWINE_PASSWORD`
|
|
293
|
+
in this repository or its `pypi` environment. Create that environment in the repository settings;
|
|
294
|
+
it can also require an approval before a release goes out. The workflow uses `__token__` as the
|
|
295
|
+
username and does not require a trusted publisher.
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# SD Model Hub
|
|
2
|
+
|
|
3
|
+
Download and manage Stable Diffusion models, from the command line or a web UI.
|
|
4
|
+
|
|
5
|
+
- **Browse** Civitai, OpenModelDB and GitHub Releases, and download with resume, SHA256
|
|
6
|
+
checks, preview images and metadata sidecars.
|
|
7
|
+
- **Hubs:** download files or whole repositories from Hugging Face (or a mirror such as
|
|
8
|
+
hf-mirror) and ModelScope.
|
|
9
|
+
- **Library:** point SD Model Hub at your ComfyUI or Stable Diffusion WebUI model folders. It
|
|
10
|
+
identifies each model's type and base model from the safetensors header (never unpickling
|
|
11
|
+
anything), shows previews, and imports, moves, renames and deletes models together with
|
|
12
|
+
their preview images and sidecar files. Drag files into the browser to upload them.
|
|
13
|
+
|
|
14
|
+
The design, the conventions and the known gaps are in [AGENTS.md](AGENTS.md).
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install . # from a checkout; see "Building" for the web UI
|
|
20
|
+
sd-model-hub --help
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Python 3.10 or newer. The web UI is bundled into the package; users do not need Node.
|
|
24
|
+
|
|
25
|
+
## Command line
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
sd-model-hub
|
|
29
|
+
├── webui start the server and open the web UI
|
|
30
|
+
├── version | env
|
|
31
|
+
├── config show | get | set | path
|
|
32
|
+
├── source list
|
|
33
|
+
├── auth status | use <manual|oauth> | connect | disconnect
|
|
34
|
+
├── search <query> --source --kind --base-model --sort --limit
|
|
35
|
+
├── info <source> <model-id>
|
|
36
|
+
├── download
|
|
37
|
+
│ ├── model <source> <model-id> --version --file --root --dir --to
|
|
38
|
+
│ ├── url <url> --to --sha256 --name
|
|
39
|
+
│ ├── hf <repo-id> --revision --include --exclude --to
|
|
40
|
+
│ └── modelscope <repo-id> --revision --include --exclude --to
|
|
41
|
+
└── library
|
|
42
|
+
├── root list | add | remove
|
|
43
|
+
├── list [path] --root --recursive --kind
|
|
44
|
+
├── info <path> --hash
|
|
45
|
+
├── identify <path>
|
|
46
|
+
├── scan --root
|
|
47
|
+
├── import <paths...> --root --to --move --rename
|
|
48
|
+
├── move <src...> <dst>
|
|
49
|
+
├── rename <path> <new-name>
|
|
50
|
+
└── delete <paths...> --permanent --yes
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Every listing command accepts `--json`, which prints the same records the API returns.
|
|
54
|
+
`--debug` works at every level. Errors exit with a code per kind: 2 not found, 3 conflict,
|
|
55
|
+
4 invalid path or input, 5 needs a token, 6 upstream failure.
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
sd-model-hub library root add ~/ComfyUI/models --layout comfyui
|
|
61
|
+
sd-model-hub library list ~/ComfyUI/models/loras --recursive --kind lora
|
|
62
|
+
sd-model-hub search "detail tweaker" --kind lora --base-model "SDXL 1.0"
|
|
63
|
+
sd-model-hub download model civitai 122359 # into the layout's LoRA folder
|
|
64
|
+
sd-model-hub download hf stabilityai/sdxl-turbo --include "*.safetensors" --to ./sdxl-turbo
|
|
65
|
+
sd-model-hub config set sources.civitai.token <token>
|
|
66
|
+
sd-model-hub webui --port 7865
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Ctrl+C during an HTTP download pauses it and keeps the `.part` file; running the same command
|
|
70
|
+
again resumes. Hub downloads cannot pause (neither library resumes), so Ctrl+C cancels them.
|
|
71
|
+
|
|
72
|
+
## Embedding in another application
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from sd_model_hub import ModelHubServer, ModelRoot
|
|
76
|
+
|
|
77
|
+
hub = ModelHubServer(
|
|
78
|
+
data_dir="./hub-data", # database and caches
|
|
79
|
+
settings_path="./my-app/model-hub.toml", # the settings file, wherever you want it
|
|
80
|
+
model_roots=[ModelRoot("/srv/models", layout="comfyui", name="Models")],
|
|
81
|
+
lock_model_roots=True, # the user cannot add, change or remove folders
|
|
82
|
+
port=0, # any free port; a number asks for that one
|
|
83
|
+
api_prefix="/tools/model-hub", # keeps our routes clear of yours
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
url = hub.start() # returns as soon as it is listening, e.g. http://127.0.0.1:54123/tools/model-hub
|
|
87
|
+
...
|
|
88
|
+
hub.stop()
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| Option | Effect |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| `data_dir` | Where the database, caches and (by default) the settings file live |
|
|
94
|
+
| `settings_path` | The settings file on its own, apart from `data_dir` |
|
|
95
|
+
| `model_roots` | Folders of models, each with its own `layout`: `comfyui`, `sd-webui` or `custom` |
|
|
96
|
+
| `lock_model_roots` | Fixes them: the API refuses changes and the interface hides those actions |
|
|
97
|
+
| `port` | `0` any free port, a number for that one (moving up unless `strict_port`), `None` for the configured one |
|
|
98
|
+
| `api_prefix` | Serves the API, the socket and the web UI under one path |
|
|
99
|
+
| `settings` | Pins any other setting, e.g. `{"downloads": {"verify_hash": False}}`; pinned values cannot be changed in the UI |
|
|
100
|
+
| `host`, `access_token`, `open_browser`, `log_level` | As for the command line; a non-loopback host requires a token |
|
|
101
|
+
|
|
102
|
+
`hub.start()` is non-blocking and returns the URL; `hub.run()` serves in the foreground;
|
|
103
|
+
`with ModelHubServer(...) as hub:` does both ends. `hub.services` exposes the library, downloads
|
|
104
|
+
and settings for direct use, and `hub.url`, `hub.port` and `hub.running` describe the server.
|
|
105
|
+
|
|
106
|
+
The command line can also take the prefix: `sd-model-hub webui --api-prefix /tools/model-hub`.
|
|
107
|
+
|
|
108
|
+
## Settings
|
|
109
|
+
|
|
110
|
+
Settings live in `settings.toml` in the data directory (`sd-model-hub config path`). Any setting
|
|
111
|
+
can be overridden with an environment variable: `SD_MODEL_HUB_<GROUP>__<FIELD>`, for example
|
|
112
|
+
`SD_MODEL_HUB_SERVER__PORT=8000` or `SD_MODEL_HUB_NETWORK__PROXY=http://127.0.0.1:7890`.
|
|
113
|
+
`SD_MODEL_HUB_DATA_DIR` moves the data directory. Tokens are never returned by the API.
|
|
114
|
+
|
|
115
|
+
The server listens on `127.0.0.1` by default. To listen on another address, set
|
|
116
|
+
`server.access_token` first; every request then needs it.
|
|
117
|
+
|
|
118
|
+
### Civitai authentication
|
|
119
|
+
|
|
120
|
+
Two ways, side by side. Neither replaces the other, and nothing switches between them by itself.
|
|
121
|
+
|
|
122
|
+
**A personal API token** (the default, and all most people need):
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
sd-model-hub config set sources.civitai.token <token> # or SD_MODEL_HUB_SOURCES__CIVITAI__TOKEN
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**A connected account** (OAuth, Authorization Code with PKCE). It needs an OAuth application
|
|
129
|
+
registered with Civitai, because the client id identifies *this installation*:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
sd-model-hub config set auth.civitai.oauth_client_id <client id>
|
|
133
|
+
sd-model-hub webui # then Settings → Civitai authentication → Connect
|
|
134
|
+
sd-model-hub auth status # which credential is in use, and where it is kept
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Register the callback with Civitai exactly as the server uses it, by default
|
|
138
|
+
`http://127.0.0.1:7865/api/v1/auth/civitai/callback`. On another port or host, or when working
|
|
139
|
+
on the UI with the Vite dev server, list the exact URLs:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
sd-model-hub 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"]'
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Tokens stay on the server: in the operating system's credential store when there is one, else a
|
|
146
|
+
file in the data directory readable only by you. They are never in `settings.toml`, never sent
|
|
147
|
+
to the browser, and never written into download records. Access tokens are refreshed about a
|
|
148
|
+
minute before they expire, once even if several downloads ask at the same time, and the rotated
|
|
149
|
+
pair is stored as a whole. `sd-model-hub auth disconnect` revokes the authorization and forgets
|
|
150
|
+
it locally, leaving any API token untouched.
|
|
151
|
+
|
|
152
|
+
An environment variable takes precedence over both, and the settings page says so while it does.
|
|
153
|
+
|
|
154
|
+
### Symbolic links
|
|
155
|
+
|
|
156
|
+
A model folder whose contents are symbolic links to another disk — as a WebUI install often has
|
|
157
|
+
for its LoRAs — needs one setting, because links that leave the model folder are refused by
|
|
158
|
+
default:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
sd-model-hub config set library.follow_symlinks true
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
It is also in Settings, under Content & Library. With it on, linked folders open and keep the
|
|
165
|
+
path you navigate with; deleting, moving and renaming then act on the files where they really
|
|
166
|
+
are. Paths containing `..`, absolute paths and reserved names are still refused in either mode,
|
|
167
|
+
and a folder reached twice through a loop of links is walked once.
|
|
168
|
+
|
|
169
|
+
## Files SD Model Hub writes next to a model
|
|
170
|
+
|
|
171
|
+
| File | Purpose |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `<name>.sdmodelhub.json` | Source metadata: model and version ids, base model, trigger words, SHA256 |
|
|
174
|
+
| `<name>.preview.<ext>` | Preview image, found by the WebUI's own lookup |
|
|
175
|
+
| `<name>.json` | Only when `downloads.write_webui_metadata` is on, and only if absent: the WebUI's user metadata |
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pip install -e ".[dev]"
|
|
181
|
+
python scripts/dev.py # list the developer tasks
|
|
182
|
+
python scripts/dev.py dev # API server and web UI together, with hot reload
|
|
183
|
+
python scripts/dev.py check # what CI runs: lint, ty, tests, generated types
|
|
184
|
+
python scripts/dev.py typecheck-py # Python types; targets Python 3.10 by default
|
|
185
|
+
python scripts/dev.py test # pytest, vitest and vue-tsc
|
|
186
|
+
python scripts/dev.py format # ruff fixes and formatting
|
|
187
|
+
python scripts/dev.py typegen # regenerate src/api/schema.d.ts from the OpenAPI schema
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`dev` is the one to use while working on the UI: it starts the API server and the Vite dev
|
|
191
|
+
server together, tags each line of output with `api` or `web`, and stops both on Ctrl+C. Open
|
|
192
|
+
the address it prints (http://localhost:5173 by default); Vite proxies `/api` and `/ws` to the
|
|
193
|
+
API server, so one address serves both and the UI hot-reloads.
|
|
194
|
+
|
|
195
|
+
While the API server is restarting, the page's socket reconnects on its own and the proxy says
|
|
196
|
+
so once:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
[api proxy] http://127.0.0.1:7865 is not answering (ECONNREFUSED). Start it with: sd-model-hub webui --no-open
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Data reappears by itself once the server is back; there is no need to reload the page.
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
python scripts/dev.py dev --api-port 8000 --web-port 3000 --data-dir ./dev-data
|
|
206
|
+
python scripts/dev.py web-dev # only the UI, against an API server you started yourself
|
|
207
|
+
sd-model-hub webui --no-open # only the API server
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The tasks are a small Python script rather than a Makefile, so they work the same on Windows.
|
|
211
|
+
Each one prints the command it runs, so the underlying tools stay easy to call directly.
|
|
212
|
+
|
|
213
|
+
The web UI uses [bun](https://bun.sh). Layers: `sd_model_hub/core` holds all logic and imports
|
|
214
|
+
no web or CLI framework (a test enforces it); `sd_model_hub/api` (FastAPI) and `sd_model_hub/cli`
|
|
215
|
+
(Typer) are thin wrappers over it.
|
|
216
|
+
|
|
217
|
+
Detection rules are JSON files in `sd_model_hub/core/detection/rules_data/`. To add a test
|
|
218
|
+
fixture from a real file: `python scripts/extract_header_fixture.py <file> <name>`.
|
|
219
|
+
|
|
220
|
+
## Building
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
python scripts/build_wheel.py # web UI with bun, then the wheel and sdist into dist/
|
|
224
|
+
python scripts/build_wheel.py --ci # for CI: skips the type-check, keeps the built web UI
|
|
225
|
+
python scripts/build_wheel.py --outdir out --keep-web-dist
|
|
226
|
+
python scripts/dev.py wheel # the same, through the task runner
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
A bare `python -m build` produces a wheel without the web UI, because setuptools packages only
|
|
230
|
+
the files that exist when it runs; the server then logs a warning and serves the API only.
|
|
231
|
+
|
|
232
|
+
## Releasing
|
|
233
|
+
|
|
234
|
+
`.github/workflows/release.yml` publishes to PyPI when a push to `main` changes
|
|
235
|
+
`sd_model_hub/version.py`, when a `v*` tag is pushed, or through **Run workflow** in the Actions tab.
|
|
236
|
+
For a version change on `main`:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
python scripts/dev.py check # first, locally
|
|
240
|
+
# bump VERSION in sd_model_hub/version.py, commit
|
|
241
|
+
git push origin main
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The workflow runs the tests and ty checks on Python 3.10 to 3.14, the web UI tests and type-check, and the
|
|
245
|
+
generated-types check; for tag runs it verifies that the tag matches `sd_model_hub/version.py`, then builds
|
|
246
|
+
the wheel **with the web UI in it**, checks the wheel really contains the UI and the detection
|
|
247
|
+
rules, installs it into a clean environment and runs `sd-model-hub version`. Only then does it
|
|
248
|
+
publish. Tag runs also create the GitHub release with the built files attached.
|
|
249
|
+
|
|
250
|
+
Publishing runs `python -m twine upload` directly on the runner, without a Docker action.
|
|
251
|
+
Existing files are skipped so retrying a release does not upload them again.
|
|
252
|
+
|
|
253
|
+
Set-up, once: create a PyPI API token and save it as the GitHub Actions secret `TWINE_PASSWORD`
|
|
254
|
+
in this repository or its `pypi` environment. Create that environment in the repository settings;
|
|
255
|
+
it can also require an approval before a release goes out. The workflow uses `__token__` as the
|
|
256
|
+
username and does not require a trusted publisher.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "pip", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sd-model-hub"
|
|
7
|
+
description = "Download and manage Stable Diffusion models from the command line and a web UI"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dynamic = ["version"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Environment :: Console",
|
|
14
|
+
"Environment :: Web Environment",
|
|
15
|
+
"Intended Audience :: End Users/Desktop",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Topic :: Multimedia :: Graphics",
|
|
23
|
+
"Topic :: Utilities",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"fastapi",
|
|
27
|
+
"uvicorn",
|
|
28
|
+
# uvicorn speaks WebSocket only when one of these is installed. Without it the socket falls
|
|
29
|
+
# back to long polling, which works but is slower and noisier.
|
|
30
|
+
"websockets>=12",
|
|
31
|
+
"python-socketio",
|
|
32
|
+
"pydantic>=2",
|
|
33
|
+
"httpx",
|
|
34
|
+
"huggingface_hub",
|
|
35
|
+
"modelscope",
|
|
36
|
+
"typer>=0.26.7",
|
|
37
|
+
"rich",
|
|
38
|
+
"Pillow>=10.0",
|
|
39
|
+
"send2trash",
|
|
40
|
+
"tomli-w",
|
|
41
|
+
"tomli; python_version < '3.11'",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
# ty resolves the optional credential store and the Python 3.10 TOML fallback on every host.
|
|
46
|
+
dev = ["keyring", "pytest", "ruff", "tomli", "ty"]
|
|
47
|
+
|
|
48
|
+
[project.scripts]
|
|
49
|
+
"sd-model-hub" = "sd_model_hub.cli.app:main"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["."]
|
|
53
|
+
include = ["sd_model_hub*"]
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.package-data]
|
|
56
|
+
"sd_model_hub.webui" = ["dist/**/*"]
|
|
57
|
+
"sd_model_hub.core.detection" = ["rules_data/*.json"]
|
|
58
|
+
|
|
59
|
+
[tool.setuptools.dynamic]
|
|
60
|
+
version = { attr = "sd_model_hub.version.VERSION" }
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 180
|
|
64
|
+
indent-width = 4
|
|
65
|
+
extend-exclude = ["sd_model_hub/webui/dist", "sd_model_hub/webui/node_modules"]
|
|
66
|
+
lint.ignore = [
|
|
67
|
+
"E402" # module-import-not-at-top-of-file
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.ty.environment]
|
|
71
|
+
python-version = "3.10"
|
|
72
|
+
|
|
73
|
+
[tool.ty.terminal]
|
|
74
|
+
output-format = "concise"
|
|
75
|
+
|
|
76
|
+
[tool.ty.src]
|
|
77
|
+
include = ["sd_model_hub"]
|
|
78
|
+
exclude = ["sd_model_hub/webui/dist", "sd_model_hub/webui/node_modules"]
|
|
79
|
+
|
|
80
|
+
[tool.pytest.ini_options]
|
|
81
|
+
testpaths = ["tests"]
|
|
82
|
+
markers = [
|
|
83
|
+
"live: tests that call real remote services (deselected by default)",
|
|
84
|
+
]
|
|
85
|
+
addopts = "-m 'not live'"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""SD Model Hub: download and manage Stable Diffusion models.
|
|
2
|
+
|
|
3
|
+
To embed the web UI and API in another application:
|
|
4
|
+
|
|
5
|
+
from sd_model_hub import ModelHubServer, ModelRoot
|
|
6
|
+
|
|
7
|
+
hub = ModelHubServer(model_roots=[ModelRoot("/srv/models", layout="comfyui")], port=0)
|
|
8
|
+
print(hub.start()) # http://127.0.0.1:54123
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
from sd_model_hub.version import VERSION
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from sd_model_hub.embed import ModelHubServer, ModelRoot, serve
|
|
17
|
+
|
|
18
|
+
__all__ = ["VERSION", "ModelHubServer", "ModelRoot", "serve"]
|
|
19
|
+
|
|
20
|
+
_LAZY = {"ModelHubServer", "ModelRoot", "serve"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def __getattr__(name: str) -> Any:
|
|
24
|
+
"""Import the server lazily, so ``sd-model-hub version`` does not pay for FastAPI."""
|
|
25
|
+
if name in _LAZY:
|
|
26
|
+
from sd_model_hub import embed
|
|
27
|
+
|
|
28
|
+
return getattr(embed, name)
|
|
29
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def __dir__() -> list[str]:
|
|
33
|
+
return sorted(__all__)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""FastAPI wrapper over ``sd_model_hub.core``."""
|