💬 Commit message: Update 2026-02-15 18:38:39, 2 files, 121 lines
📁 Files changed: 2 📝 Lines changed: 121 • README.md • civitai_routes.py
This commit is contained in:
@@ -46,6 +46,15 @@ tsr search -t lora -b sdxl
|
|||||||
|
|
||||||
# Sort by newest, limit results
|
# Sort by newest, limit results
|
||||||
tsr search -t checkpoint -s newest -n 10
|
tsr search -t checkpoint -s newest -n 10
|
||||||
|
|
||||||
|
# Filter by tag and period
|
||||||
|
tsr search --tag anime -p week -b illustrious
|
||||||
|
|
||||||
|
# By creator
|
||||||
|
tsr search -u "username"
|
||||||
|
|
||||||
|
# SFW only with commercial use filter
|
||||||
|
tsr search --sfw --commercial sell
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get Model Info
|
### Get Model Info
|
||||||
@@ -240,9 +249,10 @@ local = "http://localhost:51200"
|
|||||||
default_remote = "junkpile"
|
default_remote = "junkpile"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or set API key via environment variable:
|
Or set API keys via environment variables:
|
||||||
```bash
|
```bash
|
||||||
export CIVITAI_API_KEY="your-api-key"
|
export CIVITAI_API_KEY="your-api-key" # For CivitAI API access
|
||||||
|
export TENSORS_API_KEY="your-server-key" # For server authentication
|
||||||
```
|
```
|
||||||
|
|
||||||
## Default Paths
|
## Default Paths
|
||||||
@@ -263,9 +273,16 @@ Data is stored in XDG-compliant paths:
|
|||||||
| Option | Values |
|
| Option | Values |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| `-t, --type` | checkpoint, lora, embedding, vae, controlnet, locon |
|
| `-t, --type` | checkpoint, lora, embedding, vae, controlnet, locon |
|
||||||
| `-b, --base` | sd15, sdxl, pony, flux, illustrious |
|
| `-b, --base` | sd14, sd15, sd2, sdxl, pony, flux, illustrious, noobai, auraflow |
|
||||||
| `-s, --sort` | downloads, rating, newest |
|
| `-s, --sort` | downloads, rating, newest |
|
||||||
| `-n, --limit` | Number of results (default: 20) |
|
| `-n, --limit` | Number of results (default: 25) |
|
||||||
|
| `-p, --period` | all, year, month, week, day |
|
||||||
|
| `--tag` | Filter by tag (e.g., "anime") |
|
||||||
|
| `-u, --user` | Filter by creator username |
|
||||||
|
| `--nsfw` | none, soft, mature, x |
|
||||||
|
| `--sfw` | Exclude NSFW content |
|
||||||
|
| `--commercial` | none, image, rent, sell |
|
||||||
|
| `--page` | Page number for pagination |
|
||||||
|
|
||||||
## Generate Options
|
## Generate Options
|
||||||
|
|
||||||
@@ -288,9 +305,16 @@ Data is stored in XDG-compliant paths:
|
|||||||
|
|
||||||
When running `tsr serve`, the following endpoints are available:
|
When running `tsr serve`, the following endpoints are available:
|
||||||
|
|
||||||
|
**OpenAPI Documentation:** Visit `/docs` for interactive Scalar API documentation.
|
||||||
|
|
||||||
|
**Authentication:** If `TENSORS_API_KEY` is set, all endpoints except `/status` and `/docs` require authentication via:
|
||||||
|
- Header: `X-API-Key: your-key`
|
||||||
|
- Query param: `?api_key=your-key`
|
||||||
|
|
||||||
| Endpoint | Method | Description |
|
| Endpoint | Method | Description |
|
||||||
|----------|--------|-------------|
|
|----------|--------|-------------|
|
||||||
| `/status` | GET | Server status and active model |
|
| `/status` | GET | Server status (public) |
|
||||||
|
| `/docs` | GET | OpenAPI documentation (public) |
|
||||||
| `/reload` | POST | Hot-reload with new model |
|
| `/reload` | POST | Hot-reload with new model |
|
||||||
| `/api/images` | GET | List gallery images |
|
| `/api/images` | GET | List gallery images |
|
||||||
| `/api/images/{id}` | GET | Get image file |
|
| `/api/images/{id}` | GET | Get image file |
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from enum import Enum
|
||||||
|
from typing import Annotated, Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from fastapi import APIRouter, Query, Response
|
from fastapi import APIRouter, Query, Response
|
||||||
@@ -17,6 +18,42 @@ logger = logging.getLogger(__name__)
|
|||||||
router = APIRouter(prefix="/api/civitai", tags=["CivitAI"])
|
router = APIRouter(prefix="/api/civitai", tags=["CivitAI"])
|
||||||
|
|
||||||
|
|
||||||
|
class SortOrder(str, Enum):
|
||||||
|
"""Sort order options for CivitAI search."""
|
||||||
|
|
||||||
|
most_downloaded = "Most Downloaded"
|
||||||
|
highest_rated = "Highest Rated"
|
||||||
|
newest = "Newest"
|
||||||
|
|
||||||
|
|
||||||
|
class Period(str, Enum):
|
||||||
|
"""Time period filter options."""
|
||||||
|
|
||||||
|
all = "AllTime"
|
||||||
|
year = "Year"
|
||||||
|
month = "Month"
|
||||||
|
week = "Week"
|
||||||
|
day = "Day"
|
||||||
|
|
||||||
|
|
||||||
|
class NsfwLevel(str, Enum):
|
||||||
|
"""NSFW content filter levels."""
|
||||||
|
|
||||||
|
none = "None"
|
||||||
|
soft = "Soft"
|
||||||
|
mature = "Mature"
|
||||||
|
x = "X"
|
||||||
|
|
||||||
|
|
||||||
|
class CommercialUse(str, Enum):
|
||||||
|
"""Commercial use filter options."""
|
||||||
|
|
||||||
|
none = "None"
|
||||||
|
image = "Image"
|
||||||
|
rent = "Rent"
|
||||||
|
sell = "Sell"
|
||||||
|
|
||||||
|
|
||||||
def _get_headers(api_key: str | None) -> dict[str, str]:
|
def _get_headers(api_key: str | None) -> dict[str, str]:
|
||||||
"""Get headers for CivitAI API requests."""
|
"""Get headers for CivitAI API requests."""
|
||||||
headers: dict[str, str] = {}
|
headers: dict[str, str] = {}
|
||||||
@@ -27,28 +64,56 @@ def _get_headers(api_key: str | None) -> dict[str, str]:
|
|||||||
|
|
||||||
@router.get("/search", response_model=None)
|
@router.get("/search", response_model=None)
|
||||||
async def search_models(
|
async def search_models(
|
||||||
query: str | None = Query(default=None, description="Search query"),
|
query: Annotated[str | None, Query(description="Search query")] = None,
|
||||||
types: str | None = Query(default=None, description="Model type (Checkpoint, LORA, LoCon, etc.)"),
|
types: Annotated[str | None, Query(description="Model type (Checkpoint, LORA, etc.)")] = None,
|
||||||
base_models: str | None = Query(default=None, alias="baseModels", description="Base model (SD 1.5, SDXL 1.0, Pony, etc.)"),
|
base_models: Annotated[str | None, Query(alias="baseModels", description="Base model")] = None,
|
||||||
sort: str = Query(default="Most Downloaded", description="Sort order"),
|
sort: Annotated[SortOrder, Query(description="Sort order")] = SortOrder.most_downloaded,
|
||||||
limit: int = Query(default=20, le=100, description="Max results"),
|
limit: Annotated[int | None, Query(le=100, description="Max results (default: 25)", example=5)] = None,
|
||||||
nsfw: bool = Query(default=True, description="Include NSFW models"),
|
period: Annotated[Period | None, Query(description="Time period filter")] = None,
|
||||||
|
tag: Annotated[str | None, Query(description="Filter by tag")] = None,
|
||||||
|
username: Annotated[str | None, Query(description="Filter by creator username")] = None,
|
||||||
|
page: Annotated[int | None, Query(ge=1, description="Page number")] = None,
|
||||||
|
nsfw: Annotated[NsfwLevel | None, Query(description="NSFW filter level")] = None,
|
||||||
|
sfw: Annotated[bool, Query(description="Exclude NSFW content")] = False,
|
||||||
|
commercial: Annotated[CommercialUse | None, Query(description="Commercial use filter")] = None,
|
||||||
) -> dict[str, Any] | Response:
|
) -> dict[str, Any] | Response:
|
||||||
"""Search CivitAI models."""
|
"""Search CivitAI models.
|
||||||
|
|
||||||
|
Supports all CivitAI search parameters including filters for type, base model,
|
||||||
|
time period, tags, creator, NSFW level, and commercial use.
|
||||||
|
"""
|
||||||
api_key = load_api_key()
|
api_key = load_api_key()
|
||||||
|
actual_limit = limit if limit is not None else 25
|
||||||
|
|
||||||
params: dict[str, Any] = {
|
params: dict[str, Any] = {
|
||||||
"limit": min(limit, 100),
|
"limit": min(actual_limit, 100),
|
||||||
"nsfw": str(nsfw).lower(),
|
"sort": sort.value,
|
||||||
"sort": sort,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Handle NSFW filtering
|
||||||
|
if sfw:
|
||||||
|
params["nsfw"] = "false"
|
||||||
|
elif nsfw:
|
||||||
|
params["nsfwLevel"] = nsfw.value
|
||||||
|
else:
|
||||||
|
params["nsfw"] = "true" # Default: include all
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
params["query"] = query
|
params["query"] = query
|
||||||
if types:
|
if types:
|
||||||
params["types"] = types
|
params["types"] = types
|
||||||
if base_models:
|
if base_models:
|
||||||
params["baseModels"] = base_models
|
params["baseModels"] = base_models
|
||||||
|
if period:
|
||||||
|
params["period"] = period.value
|
||||||
|
if tag:
|
||||||
|
params["tag"] = tag
|
||||||
|
if username:
|
||||||
|
params["username"] = username
|
||||||
|
if page:
|
||||||
|
params["page"] = page
|
||||||
|
if commercial:
|
||||||
|
params["allowCommercialUse"] = commercial.value
|
||||||
|
|
||||||
url = f"{CIVITAI_API_BASE}/models"
|
url = f"{CIVITAI_API_BASE}/models"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user