30 Commits

Author SHA1 Message Date
Aodhan Collins
ed9a7b4b11 Updated JSON fields. 2026-03-21 18:21:00 +00:00
Aodhan Collins
32a73b02f5 Add semantic tagging, search, favourite/NSFW filtering, and LLM job queue
Replaces old list-format tags (which duplicated prompt content) with structured
dict tags per category (origin_series, outfit_type, participants, style_type,
scene_type, etc.). Tags are now purely organizational metadata — removed from
the prompt pipeline entirely.

Adds is_favourite and is_nsfw columns to all 8 resource models. Favourite is
DB-only (user preference); NSFW is mirrored in JSON tags for rescan persistence.
All library pages get filter controls and favourites-first sorting.

Introduces a parallel LLM job queue (_enqueue_task + _llm_queue_worker) for
background tag regeneration, with the same status polling UI as ComfyUI jobs.
Fixes call_llm() to use has_request_context() fallback for background threads.

Adds global search (/search) across resources and gallery images, with navbar
search bar. Adds gallery image sidecar JSON for per-image favourite/NSFW metadata.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 03:22:09 +00:00
Aodhan Collins
7d79e626a5 Add REST API for preset-based generation and fallback cover images
REST API (routes/api.py): Three endpoints behind API key auth for
programmatic image generation via presets — list presets, queue
generation with optional overrides, and poll job status.

Shared generation logic extracted from routes/presets.py into
services/generation.py so both web UI and API use the same code path.

Fallback covers: library index pages now show a random generated image
at reduced opacity when no cover is assigned, instead of "No Image".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 21:19:12 +00:00
Aodhan Collins
d756ea1d0e Updated generation pages. 2026-03-15 17:45:17 +00:00
Aodhan Collins
79bbf669e2 Updated json schema 2026-03-15 17:44:43 +00:00
Aodhan Collins
5e4348ebc1 Add extra prompts, endless generation, random character default, and small fixes
- Add extra positive/negative prompt textareas to all 9 detail pages with session persistence
- Add Endless generation button to all detail pages (continuous preview generation until stopped)
- Default character selector to "Random Character" on all secondary detail pages
- Fix queue clear endpoint (remove spurious auth check)
- Refactor app.py into routes/ and services/ modules
- Update CLAUDE.md with new architecture documentation
- Various data file updates and cleanup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:07:16 +00:00
Aodhan Collins
1b8a798c31 Add graceful fallback for MCP import in test script
- Add try/except block for MCP package import
- Provide helpful error message when MCP is not installed
- Exit gracefully with status code 1 on import failure

Addresses code review feedback.
2026-03-07 21:13:32 +00:00
Aodhan Collins
d95b81dde5 Multiple bug fixes. 2026-03-06 19:28:50 +00:00
Aodhan Collins
ec08eb5d31 Add Preset Library feature
Presets are saved generation recipes that combine all resource types
(character, outfit, action, style, scene, detailer, look, checkpoint)
with per-field on/off/random toggles. At generation time, entities
marked "random" are picked from the DB and fields marked "random" are
randomly included or excluded.

- Preset model + sync_presets() following existing category pattern
- _resolve_preset_entity() / _resolve_preset_fields() helpers
- Full route set: index, detail, generate, edit, upload, clone, save_json, create (LLM), rescan
- 4 templates: index (gallery), detail (summary + generate), edit (3-way toggle UI), create (LLM form)
- example_01.json reference preset + preset_system.txt LLM prompt
- Presets nav link in layout.html

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 23:49:24 +00:00
Aodhan Collins
2c1c3a7ed7 Merge branch 'presets'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 23:09:00 +00:00
Aodhan Collins
ee36caccd6 Sort all batch generation queues by JSON filename
All get_missing_* routes and generate_missing routes now order results
by filename (alphabetical) instead of display name or undefined order.
Checkpoint uses checkpoint_path as the equivalent sort key.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 23:08:37 +00:00
Aodhan Collins
e226548471 Merge branch 'logging'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 23:02:41 +00:00
Aodhan Collins
b9196ef5f5 Add structured logging for job queue and workflow prompts
Replace bare print() calls with Python logging module. Job lifecycle
(enqueue, start, ComfyUI acceptance, completion, failure) now emits
timestamped INFO logs to stdout, captured by Docker. Failures use
logger.exception() for full tracebacks. Workflow prompt block logs as
a single INFO entry; LoRA chain details moved to DEBUG level.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 22:55:53 +00:00
Aodhan Collins
a38915b354 Refactor UI, settings, and code quality across all categories
- Fix Replace Cover: routes now read preview_path from form POST instead of session (session writes from background threads were lost)
- Fix batch generation: submit all jobs immediately, poll all in parallel via Promise.all
- Fix label NameError in character generate route
- Fix style detail missing characters context
- Selected Preview pane: click any image to select it; data-preview-path on all images across all 8 detail templates
- Gallery → Library rename across all index page headings and navbar
- Settings: add configurable LoRA/checkpoint directories; default checkpoint selector moved from navbar to settings page
- Consolidate 6 get_available_*_loras() into single get_available_loras(category) reading from Settings
- ComfyUI tooltip shows currently loaded checkpoint name
- Remove navbar checkpoint bar
- Phase 4 cleanup: remove dead _queue_generation(), add session.modified, standardize log prefixes, rename action_type → action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 22:48:28 +00:00
Aodhan Collins
da55b0889b Add Docker support and refactor prompt/queue internals
- Add Dockerfile, docker-compose.yml, .dockerignore for containerised deployment
- Extract _resolve_character(), _ensure_character_fields(), _append_background() helpers to eliminate repeated inline character-field injection and background-tag patterns across all secondary-category generate routes
- Add _IDENTITY_KEYS / _WARDROBE_KEYS constants
- Fix build_extras_prompt() bug: detailer prompt (a list) was being appended as a single item instead of extended
- Replace all per-route _finalize closures with _make_finalize() factory, reducing duplication across 10 generate routes
- Add _prune_job_history() called each worker loop iteration to prevent unbounded memory growth
- Remove 10 orphaned legacy finalize_generation HTTP routes and check_status route (superseded by job queue API since job-queue branch)
- Remove one-time migration scripts (migrate_actions, migrate_detailers, migrate_lora_weight_range, migrate_wardrobe)
- Update CLAUDE.md and README.md to document new helpers, queue architecture, and Docker deployment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 16:46:36 +00:00
Aodhan Collins
9b143e65b1 Redesign gallery grid layout and update CSS across all index pages
Increase card density with more columns per breakpoint (2→6 across sm/xl).
Refactor style.css for consistent card sizing, spacing, and responsive layout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 21:43:40 +00:00
Aodhan Collins
27d2a70867 Merge branch 'job-queue'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 02:33:04 +00:00
Aodhan Collins
3c828a170f Add background job queue system for generation
- Implements sequential job queue with background worker thread (_enqueue_job, _queue_worker)
- All generate routes now return job_id instead of prompt_id; frontend polls /api/queue/<id>/status
- Queue management UI in navbar with live badge, job list, pause/resume/remove controls
- Fix: replaced url_for() calls inside finalize callbacks with direct string paths (url_for raises RuntimeError without request context in background threads)
- Batch cover generation now uses two-phase pattern: queue all jobs upfront, then poll concurrently via Promise.all so page navigation doesn't interrupt the process
- Strengths gallery sweep migrated to same two-phase pattern; sgStop() cancels queued jobs server-side
- LoRA weight randomisation via lora_weight_min/lora_weight_max already present in _resolve_lora_weight

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 02:32:50 +00:00
Aodhan Collins
ae7ba961c1 Add danbooru-mcp auto-start, git sync, status API endpoints, navbar status indicators, and LLM format retry
- app.py: add subprocess import; add _ensure_mcp_repo() to clone/pull
  danbooru-mcp from https://git.liveaodh.com/aodhan/danbooru-mcp into
  tools/danbooru-mcp/ at startup; add ensure_mcp_server_running() which
  calls _ensure_mcp_repo() then starts the Docker container if not running;
  add GET /api/status/comfyui and GET /api/status/mcp health endpoints;
  fix call_llm() to retry up to 3 times on unexpected response format
  (KeyError/IndexError), logging the raw response and prompting the LLM
  to respond with valid JSON before each retry
- templates/layout.html: add ComfyUI and MCP status dot indicators to
  navbar; add polling JS that checks both endpoints on load and every 30s
- static/style.css: add .service-status, .status-dot, .status-ok,
  .status-error, .status-checking styles and status-pulse keyframe animation
- .gitignore: add tools/ to exclude the cloned danbooru-mcp repo
2026-03-03 00:57:27 +00:00
Aodhan Collins
0b8802deb5 Add Checkpoints Gallery with per-checkpoint generation settings
- New Checkpoint model (slug, name, checkpoint_path, data JSON, image_path)
- sync_checkpoints() loads metadata from data/checkpoints/*.json and falls
  back to template defaults for models without a JSON file
- _apply_checkpoint_settings() applies per-checkpoint steps, CFG, sampler,
  base positive/negative prompts, and VAE (with dynamic VAELoader node
  injection for non-integrated VAEs) to the ComfyUI workflow
- Bulk Create from Checkpoints: scans Illustrious/Noob model directories,
  reads matching HTML files, uses LLM to populate metadata, falls back to
  template defaults when no HTML is present
- Gallery index with batch cover generation and WebSocket progress bar
- Detail page showing Generation Settings and Base Prompts cards
- Checkpoints nav link added to layout
- New data/prompts/checkpoint_system.txt LLM system prompt
- Updated README with all current galleries and file structure
- Also includes accumulated action/scene JSON updates, new actions, and
  other template/generator improvements from prior sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 21:25:23 +00:00
0d7d4d404f Merge pull request 'Added scene and actions browser.' (#5) from scenes-browser into master
Reviewed-on: #5
2026-02-22 23:45:22 +00:00
Aodhan Collins
615c400024 Added scene and actions browser. 2026-02-22 23:44:28 +00:00
bb65486995 Merge pull request 'Added style browser.' (#4) from style-browser into master
Reviewed-on: #4
2026-02-20 21:23:38 +00:00
Aodhan Collins
8487b177b4 Added style browser. 2026-02-20 21:22:53 +00:00
116941673e Merge pull request 'feat: implement Actions Gallery with character integration and triple LoRA chaining' (#3) from action-browser into master
Reviewed-on: #3
2026-02-19 20:07:17 +00:00
Aodhan Collins
467c90594c feat: implement Actions Gallery with character integration and triple LoRA chaining
- Added Actions gallery with CRUD and JSON sync
- Implemented Triple LoRA workflow (Character -> Outfit -> Action)
- Added character-integrated previews for Actions with style matching
- Implemented granular prompt selection and default persistence for Actions
- Added detailed development guide for extending gallery features
2026-02-19 20:06:57 +00:00
a4a21051a5 Merge pull request 'Add outfit gallery and AI-powered creation for characters and outfits' (#2) from clothing-gallery into master
Reviewed-on: #2
2026-02-19 18:36:02 +00:00
Aodhan Collins
c0e6cff7b7 Add outfit gallery and AI-powered creation for characters and outfits
- Add outfit gallery with CRUD operations (create, read, update, delete)
- Add AI-powered profile generation for both characters and outfits
- Add toggle to switch between AI generation and manual creation
- Auto-generate filenames from names with incrementing for duplicates
- Add 'full_body' and 'bottom' fields to wardrobe structure
- Update all character and outfit JSON files with new wardrobe fields
- Reorganize data into data/characters and data/clothing directories
- Update README with new features and JSON structure documentation
2026-02-19 18:34:46 +00:00
369c92e3ea Merge pull request 'Expanded generation options. Multiple outfits support.' (#1) from expanded-generation into master
Reviewed-on: #1
2026-02-19 00:41:30 +00:00
Aodhan Collins
5aede18ad5 Expanded generation options. Multiple outfits support. 2026-02-19 00:40:29 +00:00
1330 changed files with 55531 additions and 2899 deletions

8
.dockerignore Normal file
View File

@@ -0,0 +1,8 @@
venv/
__pycache__/
*.pyc
instance/
flask_session/
static/uploads/
tools/
.git/

3
.gitignore vendored
View File

@@ -31,3 +31,6 @@ Thumbs.db
# Logs # Logs
*.log *.log
# Tools (cloned at runtime — not tracked in this repo)
tools/

219
API_GUIDE.md Normal file
View File

@@ -0,0 +1,219 @@
# GAZE REST API Guide
## Setup
1. Open **Settings** in the GAZE web UI
2. Scroll to **REST API Key** and click **Regenerate**
3. Copy the key — you'll need it for all API requests
## Authentication
Every request must include your API key via one of:
- **Header (recommended):** `X-API-Key: <your-key>`
- **Query parameter:** `?api_key=<your-key>`
Responses for auth failures:
| Status | Meaning |
|--------|---------|
| `401` | Missing API key |
| `403` | Invalid API key |
## Endpoints
### List Presets
```
GET /api/v1/presets
```
Returns all available presets.
**Response:**
```json
{
"presets": [
{
"preset_id": "example_01",
"slug": "example_01",
"name": "Example Preset",
"has_cover": true
}
]
}
```
### Generate Image
```
POST /api/v1/generate/<preset_slug>
```
Queue one or more image generations using a preset's configuration. All body parameters are optional — when omitted, the preset's own settings are used.
**Request body (JSON):**
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `count` | int | `1` | Number of images to generate (120) |
| `checkpoint` | string | — | Override checkpoint path (e.g. `"Illustrious/model.safetensors"`) |
| `extra_positive` | string | `""` | Additional positive prompt tags appended to the generated prompt |
| `extra_negative` | string | `""` | Additional negative prompt tags |
| `seed` | int | random | Fixed seed for reproducible generation |
| `width` | int | — | Output width in pixels (must provide both width and height) |
| `height` | int | — | Output height in pixels (must provide both width and height) |
**Response (202):**
```json
{
"jobs": [
{ "job_id": "783f0268-ba85-4426-8ca2-6393c844c887", "status": "queued" }
]
}
```
**Errors:**
| Status | Cause |
|--------|-------|
| `400` | Invalid parameters (bad count, seed, or mismatched width/height) |
| `404` | Preset slug not found |
| `500` | Internal generation error |
### Check Job Status
```
GET /api/v1/job/<job_id>
```
Poll this endpoint to track generation progress.
**Response:**
```json
{
"id": "783f0268-ba85-4426-8ca2-6393c844c887",
"label": "Preset: Example Preset preview",
"status": "done",
"error": null,
"result": {
"image_url": "/static/uploads/presets/example_01/gen_1773601346.png",
"relative_path": "presets/example_01/gen_1773601346.png",
"seed": 927640517599332
}
}
```
**Job statuses:**
| Status | Meaning |
|--------|---------|
| `pending` | Waiting in queue |
| `processing` | Currently generating |
| `done` | Complete — `result` contains image info |
| `failed` | Error occurred — check `error` field |
The `result` object is only present when status is `done`. Use `seed` from the result to reproduce the exact same image later.
**Retrieving the image:** The `image_url` is a path relative to the server root. Fetch it directly:
```
GET http://<host>:5782/static/uploads/presets/example_01/gen_1773601346.png
```
Image retrieval does not require authentication.
## Examples
### Generate a single image and wait for it
```bash
API_KEY="your-key-here"
HOST="http://localhost:5782"
# Queue generation
JOB_ID=$(curl -s -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{}' \
"$HOST/api/v1/generate/example_01" | python3 -c "import sys,json; print(json.load(sys.stdin)['jobs'][0]['job_id'])")
echo "Job: $JOB_ID"
# Poll until done
while true; do
RESULT=$(curl -s -H "X-API-Key: $API_KEY" "$HOST/api/v1/job/$JOB_ID")
STATUS=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
echo "Status: $STATUS"
if [ "$STATUS" = "done" ] || [ "$STATUS" = "failed" ]; then
echo "$RESULT" | python3 -m json.tool
break
fi
sleep 5
done
```
### Generate 3 images with extra prompts
```bash
curl -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"count": 3,
"extra_positive": "smiling, outdoors",
"extra_negative": "blurry"
}' \
"$HOST/api/v1/generate/example_01"
```
### Reproduce a specific image
```bash
curl -X POST \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"seed": 927640517599332}' \
"$HOST/api/v1/generate/example_01"
```
### Python example
```python
import requests
import time
HOST = "http://localhost:5782"
API_KEY = "your-key-here"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
# List presets
presets = requests.get(f"{HOST}/api/v1/presets", headers=HEADERS).json()
print(f"Available presets: {[p['name'] for p in presets['presets']]}")
# Generate
resp = requests.post(
f"{HOST}/api/v1/generate/{presets['presets'][0]['slug']}",
headers=HEADERS,
json={"count": 1},
).json()
job_id = resp["jobs"][0]["job_id"]
print(f"Queued job: {job_id}")
# Poll
while True:
status = requests.get(f"{HOST}/api/v1/job/{job_id}", headers=HEADERS).json()
print(f"Status: {status['status']}")
if status["status"] in ("done", "failed"):
break
time.sleep(5)
if status["status"] == "done":
image_url = f"{HOST}{status['result']['image_url']}"
print(f"Image: {image_url}")
print(f"Seed: {status['result']['seed']}")
```

598
CLAUDE.md Normal file
View File

@@ -0,0 +1,598 @@
# GAZE — Character Browser: LLM Development Guide
## What This Project Is
GAZE is a Flask web app for managing AI image generation assets and generating images via ComfyUI. It is a **personal creative tool** for organizing characters, outfits, actions, styles, scenes, and detailers — all of which map to Stable Diffusion LoRAs and prompt fragments — and generating images by wiring those assets into a ComfyUI workflow at runtime.
The app is deployed locally, connects to a local ComfyUI instance at `http://127.0.0.1:8188`, and uses SQLite for persistence. LoRA and model files live on `/mnt/alexander/AITools/Image Models/`.
---
## Architecture
### File Structure
```
app.py # ~186 lines: Flask init, config, logging, route registration, startup/migrations
models.py # SQLAlchemy models only
comfy_workflow.json # ComfyUI workflow template with placeholder strings
utils.py # Pure constants + helpers (no Flask/DB deps)
services/
__init__.py
comfyui.py # ComfyUI HTTP client (queue_prompt, get_history, get_image)
workflow.py # Workflow building (_prepare_workflow, _apply_checkpoint_settings)
prompts.py # Prompt building + dedup (build_prompt, build_extras_prompt)
llm.py # LLM integration + MCP tool calls (call_llm, load_prompt)
mcp.py # MCP/Docker server lifecycle (ensure_mcp_server_running)
sync.py # All sync_*() functions + preset resolution helpers
job_queue.py # Background job queue (_enqueue_job, _make_finalize, worker thread)
file_io.py # LoRA/checkpoint scanning, file helpers
generation.py # Shared generation logic (generate_from_preset)
routes/
__init__.py # register_routes(app) — imports and calls all route modules
characters.py # Character CRUD + generation + outfit management
outfits.py # Outfit routes
actions.py # Action routes
styles.py # Style routes
scenes.py # Scene routes
detailers.py # Detailer routes
checkpoints.py # Checkpoint routes
looks.py # Look routes
presets.py # Preset routes
generator.py # Generator mix-and-match page
gallery.py # Gallery browsing + image/resource deletion
settings.py # Settings page + status APIs + context processors
strengths.py # Strengths gallery system
transfer.py # Resource transfer system
queue_api.py # /api/queue/* endpoints
api.py # REST API v1 (preset generation, auth)
regenerate.py # Tag regeneration (single + bulk, via LLM queue)
search.py # Global search across resources and gallery images
```
### Dependency Graph
```
app.py
├── models.py (unchanged)
├── utils.py (no deps except stdlib)
├── services/
│ ├── comfyui.py ← utils (for config)
│ ├── prompts.py ← utils, models
│ ├── workflow.py ← prompts, utils, models
│ ├── llm.py ← mcp (for tool calls)
│ ├── mcp.py ← (stdlib only: subprocess, os)
│ ├── sync.py ← models, utils
│ ├── job_queue.py ← comfyui, models
│ ├── file_io.py ← models, utils
│ └── generation.py ← prompts, workflow, job_queue, sync, models
└── routes/
├── All route modules ← services/*, utils, models
└── (routes never import from other routes)
```
**No circular imports**: routes → services → utils/models. Services never import routes. Utils never imports services.
### Route Registration Pattern
Routes use a `register_routes(app)` closure pattern — each route module defines a function that receives the Flask `app` object and registers routes via `@app.route()` closures. This preserves all existing `url_for()` endpoint names without requiring Blueprint prefixes. Helper functions used only by routes in that module are defined inside `register_routes()` before the routes that reference them.
### Database
SQLite at `instance/database.db`, managed by Flask-SQLAlchemy. The DB is a cache of the JSON files on disk — the JSON files are the source of truth.
**Models**: `Character`, `Look`, `Outfit`, `Action`, `Style`, `Scene`, `Detailer`, `Checkpoint`, `Settings`
The `Settings` model stores LLM provider config, LoRA/checkpoint directory paths, default checkpoint, and `api_key` for REST API authentication.
All category models (except Settings and Checkpoint) share this pattern:
- `{entity}_id` — canonical ID (from JSON, often matches filename without extension)
- `slug` — URL-safe version of the ID (alphanumeric + underscores only, via `re.sub(r'[^a-zA-Z0-9_]', '', id)`)
- `name` — display name
- `filename` — original JSON filename
- `data` — full JSON blob (SQLAlchemy JSON column)
- `default_fields` — list of `section::key` strings saved as the user's preferred prompt fields
- `image_path` — relative path under `static/uploads/`
- `is_favourite` — boolean (DB-only, not in JSON; toggled from detail pages)
- `is_nsfw` — boolean (mirrored in both DB column and JSON `tags.nsfw`; synced on rescan)
### Data Flow: JSON → DB → Prompt → ComfyUI
1. **JSON files** in `data/{characters,clothing,actions,styles,scenes,detailers,looks}/` are loaded by `sync_*()` functions into SQLite.
2. At generation time, `build_prompt(data, selected_fields, default_fields, active_outfit)` converts the character JSON blob into `{"main": ..., "face": ..., "hand": ...}` prompt strings.
3. `_prepare_workflow(workflow, character, prompts, ...)` wires prompts and LoRAs into the loaded `comfy_workflow.json`.
4. `queue_prompt(workflow, client_id)` POSTs the workflow to ComfyUI's `/prompt` endpoint.
5. The app polls `get_history(prompt_id)` and retrieves the image via `get_image(filename, subfolder, type)`.
---
## ComfyUI Workflow Node Map
The workflow (`comfy_workflow.json`) uses string node IDs. These are the critical nodes:
| Node | Role |
|------|------|
| `3` | Main KSampler |
| `4` | Checkpoint loader |
| `5` | Empty latent (width/height) |
| `6` | Positive prompt — contains `{{POSITIVE_PROMPT}}` placeholder |
| `7` | Negative prompt |
| `8` | VAE decode |
| `9` | Save image |
| `11` | Face ADetailer |
| `13` | Hand ADetailer |
| `14` | Face detailer prompt — contains `{{FACE_PROMPT}}` placeholder |
| `15` | Hand detailer prompt — contains `{{HAND_PROMPT}}` placeholder |
| `16` | Character LoRA (or Look LoRA when a Look is active) |
| `17` | Outfit LoRA |
| `18` | Action LoRA |
| `19` | Style / Detailer / Scene LoRA (priority: style > detailer > scene) |
LoRA nodes chain: `4 → 16 → 17 → 18 → 19`. Unused LoRA nodes are bypassed by pointing `model_source`/`clip_source` directly to the prior node. All model/clip consumers (nodes 3, 6, 7, 11, 13, 14, 15) are wired to the final `model_source`/`clip_source` at the end of `_prepare_workflow`.
---
## Key Functions by Module
### `utils.py` — Constants and Pure Helpers
- **`_IDENTITY_KEYS` / `_WARDROBE_KEYS`** — Lists of canonical field names for the `identity` and `wardrobe` sections. Used by `_ensure_character_fields()`.
- **`ALLOWED_EXTENSIONS`** — Permitted upload file extensions.
- **`_LORA_DEFAULTS`** — Default LoRA directory paths per category.
- **`parse_orientation(orientation_str)`** — Converts orientation codes (`1F`, `2F`, `1M1F`, etc.) into Danbooru tags.
- **`_resolve_lora_weight(lora_data)`** — Extracts and validates LoRA weight from a lora data dict.
- **`allowed_file(filename)`** — Checks file extension against `ALLOWED_EXTENSIONS`.
### `services/prompts.py` — Prompt Building
- **`build_prompt(data, selected_fields, default_fields, active_outfit)`** — Converts a character (or combined) data dict into `{"main", "face", "hand"}` prompt strings. Field selection priority: `selected_fields``default_fields` → select all (fallback). Fields are addressed as `"section::key"` strings (e.g. `"identity::hair"`, `"wardrobe::top"`). Characters support a **nested** wardrobe format where `wardrobe` is a dict of outfit names → outfit dicts.
- **`build_extras_prompt(actions, outfits, scenes, styles, detailers)`** — Used by the Generator page. Combines prompt text from all checked items across categories into a single string.
- **`_cross_dedup_prompts(positive, negative)`** — Cross-deduplicates tags between positive and negative prompt strings. Equal counts cancel completely; excess on one side is retained.
- **`_resolve_character(character_slug)`** — Returns a `Character` ORM object for a given slug string. Handles `"__random__"` sentinel.
- **`_ensure_character_fields(character, selected_fields, ...)`** — Mutates `selected_fields` in place, appending populated identity/wardrobe keys. Called in every secondary-category generate route after `_resolve_character()`.
- **`_append_background(prompts, character=None)`** — Appends `"<primary_color> simple background"` tag to `prompts['main']`.
### `services/workflow.py` — Workflow Wiring
- **`_prepare_workflow(workflow, character, prompts, ...)`** — Core workflow wiring function. Replaces prompt placeholders, chains LoRA nodes dynamically, randomises seeds, applies checkpoint settings, runs cross-dedup as the final step.
- **`_apply_checkpoint_settings(workflow, ckpt_data)`** — Applies checkpoint-specific sampler/prompt/VAE settings.
- **`_get_default_checkpoint()`** — Returns `(checkpoint_path, checkpoint_data)` from session, database Settings, or workflow file fallback.
- **`_log_workflow_prompts(label, workflow)`** — Logs the fully assembled workflow prompts in a readable block.
### `services/job_queue.py` — Background Job Queue
Two independent queues with separate worker threads:
- **ComfyUI queue** (`_job_queue` + `_queue_worker`): Image generation jobs.
- **`_enqueue_job(label, workflow, finalize_fn)`** — Adds a generation job to the queue.
- **`_make_finalize(category, slug, db_model_class=None, action=None)`** — Factory returning a callback that retrieves the generated image from ComfyUI, saves it, and optionally updates the DB cover image.
- **LLM queue** (`_llm_queue` + `_llm_queue_worker`): LLM task jobs (tag regeneration, bulk create with overwrite).
- **`_enqueue_task(label, task_fn)`** — Adds an LLM task job. `task_fn` receives the job dict and runs inside `app.app_context()`.
- **Shared**: Both queues share `_job_history` (for status lookup by job ID) and `_job_queue_lock`.
- **`_prune_job_history(max_age_seconds=3600)`** — Removes old terminal-state jobs from memory.
- **`init_queue_worker(flask_app)`** — Stores the app reference and starts both worker threads.
### `services/comfyui.py` — ComfyUI HTTP Client
- **`queue_prompt(prompt_workflow, client_id)`** — POSTs workflow to ComfyUI's `/prompt` endpoint.
- **`get_history(prompt_id)`** — Polls ComfyUI for job completion.
- **`get_image(filename, subfolder, folder_type)`** — Retrieves generated image bytes.
- **`_ensure_checkpoint_loaded(checkpoint_path)`** — Forces ComfyUI to load a specific checkpoint.
### `services/llm.py` — LLM Integration
- **`call_llm(prompt, system_prompt)`** — OpenAI-compatible chat completion supporting OpenRouter (cloud) and Ollama/LMStudio (local). Implements a tool-calling loop (up to 10 turns) using `DANBOORU_TOOLS` via MCP Docker container. Safe to call from background threads (uses `has_request_context()` fallback for OpenRouter HTTP-Referer header).
- **`load_prompt(filename)`** — Loads system prompt text from `data/prompts/`.
- **`call_mcp_tool()`** — Synchronous wrapper for MCP tool calls.
### `services/sync.py` — Data Synchronization
- **`sync_characters()`, `sync_outfits()`, `sync_actions()`, etc.** — Load JSON files from `data/` directories into SQLite. One function per category.
- **`_sync_nsfw_from_tags(entity, data)`** — Reads `data['tags']['nsfw']` and sets `entity.is_nsfw`. Called in every sync function on both create and update paths.
- **`_resolve_preset_entity(type, id)`** / **`_resolve_preset_fields(preset_data)`** — Preset resolution helpers.
### `services/file_io.py` — File & DB Helpers
- **`get_available_loras(category)`** — Scans filesystem for available LoRA files in a category.
- **`get_available_checkpoints()`** — Scans checkpoint directories.
- **`_count_look_assignments()`** / **`_count_outfit_lora_assignments()`** — DB aggregate queries.
### `services/generation.py` — Shared Generation Logic
- **`generate_from_preset(preset, overrides=None)`** — Core preset generation function used by both the web route and the REST API. Resolves entities, builds prompts, wires the workflow, and enqueues the job. The `overrides` dict accepts: `action`, `checkpoint`, `extra_positive`, `extra_negative`, `seed`, `width`, `height`. Has no `request` or `session` dependencies.
### `services/mcp.py` — MCP/Docker Lifecycle
- **`ensure_mcp_server_running()`** — Ensures the danbooru-mcp Docker container is running.
- **`ensure_character_mcp_server_running()`** — Ensures the character-mcp Docker container is running.
### Route-local Helpers
Some helpers are defined inside a route module's `register_routes()` since they're only used by routes in that file:
- `routes/scenes.py`: `_queue_scene_generation()` — scene-specific workflow builder
- `routes/detailers.py`: `_queue_detailer_generation()` — detailer-specific generation helper
- `routes/styles.py`: `_build_style_workflow()` — style-specific workflow builder
- `routes/checkpoints.py`: `_build_checkpoint_workflow()` — checkpoint-specific workflow builder
- `routes/strengths.py`: `_build_strengths_prompts()`, `_prepare_strengths_workflow()` — strengths gallery helpers
- `routes/transfer.py`: `_create_minimal_template()` — transfer template builder
- `routes/gallery.py`: `_scan_gallery_images()`, `_enrich_with_names()`, `_parse_comfy_png_metadata()`, `_write_sidecar()` — gallery image sidecar JSON I/O
- `routes/regenerate.py`: Tag regeneration routes (single + category bulk + all), tag migration
- `routes/search.py`: `_search_resources()`, `_search_images()` — global search across resources and gallery
---
## JSON Data Schemas
### Character (`data/characters/*.json`)
```json
{
"character_id": "tifa_lockhart",
"character_name": "Tifa Lockhart",
"identity": { "base_specs": "", "hair": "", "eyes": "", "hands": "", "arms": "", "torso": "", "pelvis": "", "legs": "", "feet": "", "extra": "" },
"defaults": { "expression": "", "pose": "", "scene": "" },
"wardrobe": {
"default": { "full_body": "", "headwear": "", "top": "", "bottom": "", "legwear": "", "footwear": "", "hands": "", "gloves": "", "accessories": "" }
},
"styles": { "aesthetic": "", "primary_color": "", "secondary_color": "", "tertiary_color": "" },
"lora": { "lora_name": "Illustrious/Looks/tifa.safetensors", "lora_weight": 0.8, "lora_triggers": "" },
"tags": { "origin_series": "Final Fantasy VII", "origin_type": "game", "nsfw": false },
"participants": { "orientation": "1F", "solo_focus": "true" }
}
```
`participants` is optional; when absent, `(solo:1.2)` is injected. `orientation` is parsed by `parse_orientation()` into Danbooru tags (`1girl`, `hetero`, etc.).
### Outfit (`data/clothing/*.json`)
```json
{
"outfit_id": "french_maid_01",
"outfit_name": "French Maid",
"wardrobe": { "full_body": "", "headwear": "", "top": "", "bottom": "", "legwear": "", "footwear": "", "hands": "", "accessories": "" },
"lora": { "lora_name": "Illustrious/Clothing/maid.safetensors", "lora_weight": 0.8, "lora_triggers": "" },
"tags": { "outfit_type": "Uniform", "nsfw": false }
}
```
### Action (`data/actions/*.json`)
```json
{
"action_id": "sitting",
"action_name": "Sitting",
"action": { "full_body": "", "additional": "", "head": "", "eyes": "", "arms": "", "hands": "" },
"lora": { "lora_name": "", "lora_weight": 1.0, "lora_triggers": "" },
"tags": { "participants": "1girl", "nsfw": false }
}
```
### Scene (`data/scenes/*.json`)
```json
{
"scene_id": "beach",
"scene_name": "Beach",
"scene": { "background": "", "foreground": "", "furniture": "", "colors": "", "lighting": "", "theme": "" },
"lora": { "lora_name": "", "lora_weight": 1.0, "lora_triggers": "" },
"tags": { "scene_type": "Outdoor", "nsfw": false }
}
```
### Style (`data/styles/*.json`)
```json
{
"style_id": "watercolor",
"style_name": "Watercolor",
"style": { "artist_name": "", "artistic_style": "" },
"lora": { "lora_name": "", "lora_weight": 1.0, "lora_triggers": "" },
"tags": { "style_type": "Watercolor", "nsfw": false }
}
```
### Detailer (`data/detailers/*.json`)
```json
{
"detailer_id": "detailed_skin",
"detailer_name": "Detailed Skin",
"prompt": ["detailed skin", "pores"],
"focus": { "face": true, "hands": true },
"lora": { "lora_name": "", "lora_weight": 1.0, "lora_triggers": "" },
"tags": { "associated_resource": "skin", "adetailer_targets": ["face", "hands"], "nsfw": false }
}
```
### Look (`data/looks/*.json`)
```json
{
"look_id": "tifa_casual",
"look_name": "Tifa Casual",
"character_id": "tifa_lockhart",
"positive": "casual clothes, jeans",
"negative": "revealing",
"lora": { "lora_name": "Illustrious/Looks/tifa_casual.safetensors", "lora_weight": 0.85, "lora_triggers": "" },
"tags": { "origin_series": "Final Fantasy VII", "origin_type": "game", "nsfw": false }
}
```
Looks occupy LoRA node 16, overriding the character's own LoRA. The Look's `negative` is prepended to the workflow's negative prompt.
### Checkpoint (`data/checkpoints/*.json`)
```json
{
"checkpoint_path": "Illustrious/model.safetensors",
"checkpoint_name": "Model Display Name",
"base_positive": "anime",
"base_negative": "text, logo",
"steps": 25,
"cfg": 5,
"sampler_name": "euler_ancestral",
"scheduler": "normal",
"vae": "integrated"
}
```
Checkpoint JSONs are keyed by `checkpoint_path`. If no JSON exists for a discovered model file, `_default_checkpoint_data()` provides defaults.
---
## URL Routes
### Characters
- `GET /` — character gallery (index)
- `GET /character/<slug>` — character detail with generation UI
- `POST /character/<slug>/generate` — queue generation (AJAX or form); returns `{"job_id": ...}`
- `POST /character/<slug>/replace_cover_from_preview` — promote preview to cover
- `GET/POST /character/<slug>/edit` — edit character data
- `POST /character/<slug>/upload` — upload cover image
- `POST /character/<slug>/save_defaults` — save default field selection
- `POST /character/<slug>/outfit/switch|add|delete|rename` — manage per-character wardrobe outfits
- `GET/POST /create` — create new character (blank or LLM-generated)
- `POST /rescan` — sync DB from JSON files
### Category Pattern (Outfits, Actions, Styles, Scenes, Detailers)
Each category follows the same URL pattern:
- `GET /<category>/` — library with favourite/NSFW filter controls
- `GET /<category>/<slug>` — detail + generation UI
- `POST /<category>/<slug>/generate` — queue generation; returns `{"job_id": ...}`
- `POST /<category>/<slug>/replace_cover_from_preview`
- `GET/POST /<category>/<slug>/edit`
- `POST /<category>/<slug>/upload`
- `POST /<category>/<slug>/save_defaults`
- `POST /<category>/<slug>/favourite` — toggle `is_favourite` (AJAX)
- `POST /<category>/<slug>/clone` — duplicate entry
- `POST /<category>/<slug>/save_json` — save raw JSON (from modal editor)
- `POST /<category>/rescan`
- `POST /<category>/bulk_create` — LLM-generate entries from LoRA files on disk
### Looks
- `GET /looks` — gallery
- `GET /look/<slug>` — detail
- `GET/POST /look/<slug>/edit`
- `POST /look/<slug>/generate` — queue generation; returns `{"job_id": ...}`
- `POST /look/<slug>/replace_cover_from_preview`
- `GET/POST /look/create`
- `POST /looks/rescan`
### Generator (Mix & Match)
- `GET/POST /generator` — freeform generator with multi-select accordion UI
- `POST /generator/preview_prompt` — AJAX: preview composed prompt without generating
### Checkpoints
- `GET /checkpoints` — gallery
- `GET /checkpoint/<slug>` — detail + generation settings editor
- `POST /checkpoint/<slug>/save_json`
- `POST /checkpoints/rescan`
### REST API (`/api/v1/`)
Authenticated via `X-API-Key` header (or `api_key` query param). Key is stored in `Settings.api_key` and managed from the Settings page.
- `GET /api/v1/presets` — list all presets (id, slug, name, has_cover)
- `POST /api/v1/generate/<preset_slug>` — queue generation from a preset; accepts JSON body with optional `checkpoint`, `extra_positive`, `extra_negative`, `seed`, `width`, `height`, `count` (120); returns `{"jobs": [{"job_id": ..., "status": "queued"}]}`
- `GET /api/v1/job/<job_id>` — poll job status; returns `{"id", "label", "status", "error", "result"}`
- `POST /api/key/regenerate` — generate a new API key (Settings page)
See `API_GUIDE.md` for full usage examples.
### Job Queue API
All generation routes use the background job queue. Frontend polls:
- `GET /api/queue/<job_id>/status` — returns `{"status": "pending"|"running"|"done"|"failed", "result": {...}}`
Image retrieval is handled server-side by the `_make_finalize()` callback; there are no separate client-facing finalize routes.
### Search
- `GET /search` — global search page; query params: `q` (search term), `category` (all/characters/outfits/etc.), `nsfw` (all/sfw/nsfw), `type` (all/resources/images)
### Tag Regeneration
- `POST /api/<category>/<slug>/regenerate_tags` — single entity tag regeneration via LLM queue
- `POST /admin/bulk_regenerate_tags/<category>` — queue LLM tag regeneration for all entities in a category
- `POST /admin/bulk_regenerate_tags` — queue LLM tag regeneration for all resources across all categories
- `POST /admin/migrate_tags` — convert old list-format tags to new dict format
### Gallery Image Metadata
- `POST /gallery/image/favourite` — toggle favourite on a gallery image (writes sidecar JSON)
- `POST /gallery/image/nsfw` — toggle NSFW on a gallery image (writes sidecar JSON)
### Utilities
- `POST /set_default_checkpoint` — save default checkpoint to session and persist to `comfy_workflow.json`
- `GET /get_missing_{characters,outfits,actions,scenes,styles,detailers,looks,checkpoints}` — AJAX: list items without cover images (sorted by display name)
- `POST /generate_missing` — batch generate covers for all characters missing one (uses job queue)
- `POST /clear_all_covers` / `clear_all_{outfit,action,scene,style,detailer,look,checkpoint}_covers`
- `GET /gallery` — global image gallery browsing `static/uploads/`
- `GET/POST /settings` — LLM provider configuration
- `POST /resource/<category>/<slug>/delete` — soft (JSON only) or hard (JSON + safetensors) delete
---
## Frontend
- Bootstrap 5.3 (CDN). Custom styles in `static/style.css`.
- All templates extend `templates/layout.html`. The base layout provides:
- `{% block content %}` — main page content
- `{% block scripts %}` — additional JS at end of body
- Navbar with links to all sections
- Global default checkpoint selector (saves to session via AJAX)
- Resource delete modal (soft/hard) shared across gallery pages
- `initJsonEditor(saveUrl)` — shared JSON editor modal (simple form + raw textarea tabs)
- Context processors inject `all_checkpoints`, `default_checkpoint_path`, and `COMFYUI_WS_URL` into every template. The `random_gen_image(category, slug)` template global returns a random image path from `static/uploads/<category>/<slug>/` for use as a fallback cover when `image_path` is not set.
- **No `{% block head %}` exists** in layout.html — do not try to use it.
- Generation is async: JS submits the form via AJAX (`X-Requested-With: XMLHttpRequest`), receives a `{"job_id": ...}` response, then polls `/api/queue/<job_id>/status` every ~1.5 seconds until `status == "done"`. The server-side worker handles all ComfyUI polling and image saving via the `_make_finalize()` callback. There are no client-facing finalize HTTP routes.
- **Batch generation** (library pages): Uses a two-phase pattern:
1. **Queue phase**: All jobs are submitted upfront via sequential fetch calls, collecting job IDs
2. **Poll phase**: All jobs are polled concurrently via `Promise.all()`, updating UI as each completes
3. **Progress tracking**: Displays currently processing items in real-time using a `Set` to track active jobs
4. **Sorting**: All batch operations sort items by display `name` (not `filename`) for better UX
- **Fallback covers** (library pages): When a resource has no assigned `image_path` but has generated images in its upload folder, a random image is shown at 50% opacity (CSS class `fallback-cover`). The image changes on each page load. Resources with no generations show "No Image".
---
## LLM Integration
### System Prompts
Text files in `data/prompts/` define JSON output schemas for LLM-generated entries:
- `character_system.txt` — character JSON schema
- `outfit_system.txt` — outfit JSON schema
- `action_system.txt`, `scene_system.txt`, `style_system.txt`, `detailer_system.txt`, `look_system.txt`, `checkpoint_system.txt`
- `preset_system.txt` — preset JSON schema
- `regenerate_tags_system.txt` — tag regeneration schema (all per-category tag structures)
Used by: character/outfit/action/scene/style create forms, bulk_create routes, and tag regeneration. All system prompts include NSFW awareness preamble.
### Danbooru MCP Tools
The LLM loop in `call_llm()` provides three tools via a Docker-based MCP server (`danbooru-mcp:latest`):
- `search_tags(query, limit, category)` — prefix search
- `validate_tags(tags)` — exact-match validation
- `suggest_tags(partial, limit, category)` — autocomplete
The LLM uses these to verify and discover correct Danbooru-compatible tags for prompts.
All system prompts (`character_system.txt`, `outfit_system.txt`, `action_system.txt`, `scene_system.txt`, `style_system.txt`, `detailer_system.txt`, `look_system.txt`, `checkpoint_system.txt`) instruct the LLM to use these tools before finalising any tag values. `checkpoint_system.txt` applies them specifically to the `base_positive` and `base_negative` fields.
---
## Tagging System
Tags are **semantic metadata** for organizing and filtering resources. They are **not injected into generation prompts** — tags are purely for the UI (search, filtering, categorization).
### Tag Schema Per Category
| Category | Tag fields | Example |
|----------|-----------|---------|
| Character | `origin_series`, `origin_type`, `nsfw` | `{"origin_series": "Final Fantasy VII", "origin_type": "game", "nsfw": false}` |
| Look | `origin_series`, `origin_type`, `nsfw` | same as Character |
| Outfit | `outfit_type`, `nsfw` | `{"outfit_type": "Uniform", "nsfw": false}` |
| Action | `participants`, `nsfw` | `{"participants": "1girl, 1boy", "nsfw": true}` |
| Style | `style_type`, `nsfw` | `{"style_type": "Anime", "nsfw": false}` |
| Scene | `scene_type`, `nsfw` | `{"scene_type": "Indoor", "nsfw": false}` |
| Detailer | `associated_resource`, `adetailer_targets`, `nsfw` | `{"associated_resource": "skin", "adetailer_targets": ["face", "hands"], "nsfw": false}` |
| Checkpoint | `art_style`, `base_model`, `nsfw` | `{"art_style": "anime", "base_model": "Illustrious", "nsfw": false}` |
### Favourite / NSFW Columns
- `is_favourite` — DB-only boolean. Toggled via `POST /<category>/<slug>/favourite`. Not stored in JSON (user preference, not asset metadata).
- `is_nsfw` — DB column **and** `tags.nsfw` in JSON. Synced from JSON on rescan via `_sync_nsfw_from_tags()`. Editable from edit pages.
### Library Filtering
All library index pages support query params:
- `?favourite=on` — show only favourites
- `?nsfw=sfw|nsfw|all` — filter by NSFW status
- Results are ordered by `is_favourite DESC, name ASC` (favourites sort first).
### Gallery Image Sidecar Files
Gallery images can have per-image favourite/NSFW metadata stored in sidecar JSON files at `{image_path}.json` (e.g. `static/uploads/characters/tifa/gen_123.png.json`). Sidecar schema: `{"is_favourite": bool, "is_nsfw": bool}`.
---
## LoRA File Paths
LoRA filenames in JSON are stored as paths relative to ComfyUI's `models/lora/` root:
| Category | Path prefix | Example |
|----------|-------------|---------|
| Character / Look | `Illustrious/Looks/` | `Illustrious/Looks/tifa_v2.safetensors` |
| Outfit | `Illustrious/Clothing/` | `Illustrious/Clothing/maid.safetensors` |
| Action | `Illustrious/Poses/` | `Illustrious/Poses/sitting.safetensors` |
| Style | `Illustrious/Styles/` | `Illustrious/Styles/watercolor.safetensors` |
| Detailer | `Illustrious/Detailers/` | `Illustrious/Detailers/skin.safetensors` |
| Scene | `Illustrious/Backgrounds/` | `Illustrious/Backgrounds/beach.safetensors` |
Checkpoint paths: `Illustrious/<filename>.safetensors` or `Noob/<filename>.safetensors`.
Absolute paths on disk:
- Checkpoints: `/mnt/alexander/AITools/Image Models/Stable-diffusion/{Illustrious,Noob}/`
- LoRAs: `/mnt/alexander/AITools/Image Models/lora/Illustrious/{Looks,Clothing,Poses,Styles,Detailers,Backgrounds}/`
---
## Adding a New Category
To add a new content category (e.g. "Poses" as a separate concept from Actions), the pattern is:
1. **Model** (`models.py`): Add a new SQLAlchemy model with the standard fields.
2. **Sync function** (`services/sync.py`): Add `sync_newcategory()` following the pattern of `sync_outfits()`.
3. **Data directory** (`app.py`): Add `app.config['NEWCATEGORY_DIR'] = 'data/newcategory'`.
4. **Routes** (`routes/newcategory.py`): Create a new route module with a `register_routes(app)` function. Implement index, detail, edit, generate, replace_cover_from_preview, upload, save_defaults, clone, rescan routes. Follow `routes/outfits.py` or `routes/scenes.py` exactly.
5. **Route registration** (`routes/__init__.py`): Import and call `newcategory.register_routes(app)`.
6. **Templates**: Create `templates/newcategory/{index,detail,edit,create}.html` extending `layout.html`.
7. **Nav**: Add link to navbar in `templates/layout.html`.
8. **Startup** (`app.py`): Import and call `sync_newcategory()` in the `with app.app_context()` block.
9. **Generator page**: Add to `routes/generator.py`, `services/prompts.py` `build_extras_prompt()`, and `templates/generator.html` accordion.
---
## Session Keys
The Flask filesystem session stores:
- `default_checkpoint` — checkpoint_path string for the global default
- `prefs_{slug}` — selected_fields list for character detail page
- `preview_{slug}` — relative image path of last character preview
- `prefs_outfit_{slug}`, `preview_outfit_{slug}`, `char_outfit_{slug}` — outfit detail state
- `prefs_action_{slug}`, `preview_action_{slug}`, `char_action_{slug}` — action detail state
- `prefs_scene_{slug}`, `preview_scene_{slug}`, `char_scene_{slug}` — scene detail state
- `prefs_detailer_{slug}`, `preview_detailer_{slug}`, `char_detailer_{slug}`, `action_detailer_{slug}`, `extra_pos_detailer_{slug}`, `extra_neg_detailer_{slug}` — detailer detail state (selected fields, preview image, character, action LoRA, extra positive prompt, extra negative prompt)
- `prefs_style_{slug}`, `preview_style_{slug}`, `char_style_{slug}` — style detail state
- `prefs_look_{slug}`, `preview_look_{slug}` — look detail state
---
## Running the App
### Directly (development)
```bash
cd /mnt/alexander/Projects/character-browser
source venv/bin/activate
python app.py
```
The app runs in debug mode on port 5000 by default. ComfyUI must be running at `http://127.0.0.1:8188`.
The DB is initialised and all sync functions are called inside `with app.app_context():` at the bottom of `app.py` before `app.run()`.
### Docker
```bash
docker compose up -d
```
The compose file (`docker-compose.yml`) runs two services:
- **`danbooru-mcp`** — built from `https://git.liveaodh.com/aodhan/danbooru-mcp.git`; the MCP tag-search container used by `call_llm()`.
- **`app`** — the Flask app, exposed on host port **5782** → container port 5000.
Key environment variables set by compose:
- `COMFYUI_URL=http://10.0.0.200:8188` — points at ComfyUI on the Docker host network.
- `SKIP_MCP_AUTOSTART=true` — disables the app's built-in danbooru-mcp launch logic (compose manages it).
Volumes mounted into the app container:
- `./data`, `./static/uploads`, `./instance`, `./flask_session` — persistent app data.
- `/Volumes/ImageModels:/ImageModels:ro` — model files for checkpoint/LoRA scanning (**requires Docker Desktop file sharing enabled for `/Volumes/ImageModels`**).
- `/var/run/docker.sock` — Docker socket so the app can exec danbooru-mcp tool containers.
---
## Common Pitfalls
- **SQLAlchemy JSON mutation**: After modifying a JSON column dict in place, always call `flag_modified(obj, "data")` or the change won't be detected.
- **Dual write**: Every edit route writes back to both the DB (`db.session.commit()`) and the JSON file on disk. Both must be kept in sync.
- **Slug generation**: `re.sub(r'[^a-zA-Z0-9_]', '', id)` — note this removes hyphens and dots, not just replaces them. Character IDs like `yuna_(ff10)` become slug `yunaffx10`. This is intentional.
- **Checkpoint slugs use underscore replacement**: `re.sub(r'[^a-zA-Z0-9_]', '_', ...)` (replaces with `_`, not removes) to preserve readability in paths.
- **LoRA chaining**: If a LoRA node has no LoRA (name is empty/None), the node is skipped and `model_source`/`clip_source` pass through unchanged. Do not set the node inputs for skipped nodes.
- **AJAX detection**: `request.headers.get('X-Requested-With') == 'XMLHttpRequest'` determines whether to return JSON or redirect.
- **Session must be marked modified for JSON responses**: After setting session values in AJAX-responding routes, set `session.modified = True`.
- **Detailer `prompt` is a list**: The `prompt` field in detailer JSON is stored as a list of strings (e.g. `["detailed skin", "pores"]`), not a plain string. In generate routes, the detailer prompt is injected directly into `prompts['main']` after `build_prompt()` returns (not via tags or `build_prompt` itself).
- **`_make_finalize` action semantics**: Pass `action=None` when the route should always update the DB cover (e.g. batch generate, checkpoint generate). Pass `action=request.form.get('action')` for routes that support both "preview" (no DB update) and "replace" (update DB). The factory skips the DB write when `action` is truthy and not `"replace"`.
- **LLM queue runs without request context**: `_enqueue_task()` callbacks execute in a background thread with only `app.app_context()`. Do not access `flask.request`, `flask.session`, or other request-scoped objects inside `task_fn`. Use `has_request_context()` guard if code is shared between HTTP handlers and background tasks.
- **Tags are metadata only**: Tags (`data['tags']`) are never injected into generation prompts. They are purely for UI filtering and search. The old pattern of `parts.extend(data.get('tags', []))` in prompt building has been removed.

29
Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
FROM python:3.12-slim
# Install system deps: git (for danbooru-mcp repo clone) + docker CLI
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
ca-certificates \
&& install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \
&& chmod a+r /etc/apt/keyrings/docker.asc \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
> /etc/apt/sources.list.d/docker.list \
&& apt-get update && apt-get install -y --no-install-recommends docker-ce-cli docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Writable dirs that will typically be bind-mounted at runtime
RUN mkdir -p static/uploads instance flask_session data/characters data/clothing \
data/actions data/styles data/scenes data/detailers data/checkpoints data/looks
EXPOSE 5000
CMD ["python", "app.py"]

View File

@@ -5,16 +5,25 @@ A local web-based GUI for managing character profiles (JSON) and generating cons
## Features ## Features
- **Character Gallery**: Automatically scans your `characters/` folder and builds a searchable, sortable database. - **Character Gallery**: Automatically scans your `characters/` folder and builds a searchable, sortable database.
- **Granular Prompt Control**: Every field in your character JSON (Identity, Wardrobe, Styles) has a checkbox. You decide exactly what is sent to the AI. - **Outfit Gallery**: Manage reusable outfit presets that can be applied to any character.
- **Actions Gallery**: A library of reusable poses and actions (e.g., "Belly Dancing", "Sword Fighting") that can be previewed on any character model.
- **Styles Gallery**: Manage art style / artist LoRA presets with AI-assisted bulk creation from your styles LoRA folder.
- **Scenes Gallery**: Background and environment LoRA presets, previewable with any character.
- **Detailers Gallery**: Detail enhancement LoRA presets for fine-tuning face, hands, and other features.
- **Checkpoints Gallery**: Browse all your installed SDXL checkpoints (Illustrious & Noob families). Stores per-checkpoint generation settings (steps, CFG, sampler, VAE, base prompts) via JSON files in `data/checkpoints/`. Supports AI-assisted metadata generation from accompanying HTML files.
- **AI-Powered Creation**: Create and populate gallery entries using AI to generate profiles from descriptions or LoRA HTML files, or manually create blank templates.
- **Character-Integrated Previews**: Standalone items (Outfits/Actions/Styles/Scenes/Detailers/Checkpoints) can be previewed directly on a specific character's model.
- **Granular Prompt Control**: Every field in your JSON models (Identity, Wardrobe, Styles, Action details) has a checkbox. You decide exactly what is sent to the AI.
- **ComfyUI Integration**: - **ComfyUI Integration**:
- **SDXL Optimized**: Designed for high-quality SDXL workflows. - **SDXL Optimized**: Designed for high-quality SDXL/Illustrious workflows.
- **Localized ADetailer**: Automated Face and Hand detailing with focused prompts (e.g., only eye color and expression are sent to the face detailer). - **Localized ADetailer**: Automated Face and Hand detailing with focused prompts (e.g., only eye color and expression are sent to the face detailer).
- **LoRA Support**: Automatically detects and applies LoRAs specified in your character sheets. - **Quad LoRA Chaining**: Chains up to four distinct LoRAs (Character + Outfit + Action + Style/Detailer/Scene) sequentially in the generation workflow.
- **Per-Checkpoint Settings**: Steps, CFG, sampler name, VAE, and base prompts are applied automatically from each checkpoint's JSON profile.
- **Real-time Progress**: Live progress bars and queue status via WebSockets (with a reliable polling fallback). - **Real-time Progress**: Live progress bars and queue status via WebSockets (with a reliable polling fallback).
- **Batch Processing**: - **Batch Processing**:
- **Fill Missing**: Generate covers for every character missing one with a single click. - **Fill Missing**: Generate covers for every item missing one with a single click, across all galleries.
- **Refresh All**: Unassign all current covers and generate a fresh set for the whole collection. - **Bulk Create from LoRAs/Checkpoints**: Auto-generate JSON metadata for entire directories using an LLM.
- **Advanced Generator**: A dedicated page to mix-and-match characters with different checkpoints (Illustrious/Noob support) and custom prompt additions. - **Advanced Generator**: A dedicated mix-and-match page combining any character, outfit, action, style, scene, and detailer in a single generation.
- **Smart URLs**: Sanitized, human-readable URLs (slugs) that handle special characters and slashes gracefully. - **Smart URLs**: Sanitized, human-readable URLs (slugs) that handle special characters and slashes gracefully.
## Prerequisites ## Prerequisites
@@ -30,6 +39,20 @@ A local web-based GUI for managing character profiles (JSON) and generating cons
## Setup & Installation ## Setup & Installation
### Option A — Docker (recommended)
1. **Clone the repository.**
2. Edit `docker-compose.yml` if needed:
- Set `COMFYUI_URL` to your ComfyUI host/port.
- Adjust the `/Volumes/ImageModels` volume path to your model directory. If you're on Docker Desktop, add the path under **Settings → Resources → File Sharing** first.
3. **Start services:**
```bash
docker compose up -d
```
The app will be available at `http://localhost:5782`.
### Option B — Local (development)
1. **Clone the repository** to your local machine. 1. **Clone the repository** to your local machine.
2. **Configure Paths**: Open `app.py` and update the following variables to match your system: 2. **Configure Paths**: Open `app.py` and update the following variables to match your system:
```python ```python
@@ -45,13 +68,18 @@ A local web-based GUI for managing character profiles (JSON) and generating cons
## Usage ## Usage
### Creating Content
- **AI Generation**: Toggle "Use AI to generate profile from description" on, then describe your character, outfit, or action. The AI will generate a complete profile with appropriate tags.
- **Manual Creation**: Toggle AI generation off to create a blank template you can edit yourself.
- **Auto-naming**: Leave the filename field empty to auto-generate one from the name. If a file already exists, a number will be appended automatically.
### Gallery Management ### Gallery Management
- **Rescan**: Use the "Rescan Character Files" button if you've added new JSON files or manually edited them. - **Rescan**: Use the "Rescan" buttons if you've added new JSON files or manually edited them.
- **Save Defaults**: On a character page, select your favorite prompt combination and click "Save as Default Selection" to remember it for future quick generations. - **Save Defaults**: On any detail page, select your favorite prompt combination and click "Save as Default Selection" to remember it for future generations.
### Generation ### Generation
- **Preview**: Generates an image and shows it to you without replacing your current cover. - **Preview**: Generates an image and shows it to you without replacing your current cover.
- **Replace**: Generates an image and sets it as the character's official gallery cover. - **Replace**: Generates an image and sets it as the item's official gallery cover.
- **Clean Start**: If you want to wipe the database and all generated images to start fresh: - **Clean Start**: If you want to wipe the database and all generated images to start fresh:
```bash ```bash
./launch.sh --clean ./launch.sh --clean
@@ -59,9 +87,16 @@ A local web-based GUI for managing character profiles (JSON) and generating cons
## File Structure ## File Structure
- `/characters`: Your character JSON files. - `/data/characters`: Character JSON files.
- `/static/uploads`: Generated images (organized by character subfolders). - `/data/clothing`: Outfit preset JSON files.
- `/templates`: HTML UI using Bootstrap 5. - `/data/actions`: Action/Pose preset JSON files.
- `/data/styles`: Art style / artist LoRA JSON files.
- `/data/scenes`: Scene/background LoRA JSON files.
- `/data/detailers`: Detailer LoRA JSON files.
- `/data/checkpoints`: Per-checkpoint metadata JSON files (steps, CFG, sampler, VAE, base prompts).
- `/data/prompts`: LLM system prompts used by the AI-assisted bulk creation features.
- `/static/uploads`: Generated images (organized by subfolders).
- `app.py`: Flask backend and prompt-building logic. - `app.py`: Flask backend and prompt-building logic.
- `comfy_workflow.json`: The API-format workflow used for generations. - `comfy_workflow.json`: The API-format workflow used for generations.
- `models.py`: SQLite database schema. - `models.py`: SQLAlchemy database models.
- `DEVELOPMENT_GUIDE.md`: Architectural patterns for extending the browser.

719
app.py
View File

@@ -1,544 +1,201 @@
import os import os
import json import logging
import time from flask import Flask
import re from flask_session import Session
import requests from models import db, Settings, Look
import random
from flask import Flask, render_template, request, redirect, url_for, flash, session
from werkzeug.utils import secure_filename
from models import db, Character
app = Flask(__name__) app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = 'static/uploads' app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.config['SECRET_KEY'] = 'dev-key-123' app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-key-123')
app.config['CHARACTERS_DIR'] = 'characters' app.config['CHARACTERS_DIR'] = 'data/characters'
app.config['COMFYUI_URL'] = 'http://127.0.0.1:8188' app.config['CLOTHING_DIR'] = 'data/clothing'
app.config['ILLUSTRIOUS_MODELS_DIR'] = '/mnt/alexander/AITools/Image Models/Stable-diffusion/Illustrious/' app.config['ACTIONS_DIR'] = 'data/actions'
app.config['NOOB_MODELS_DIR'] = '/mnt/alexander/AITools/Image Models/Stable-diffusion/Noob/' app.config['STYLES_DIR'] = 'data/styles'
app.config['SCENES_DIR'] = 'data/scenes'
app.config['DETAILERS_DIR'] = 'data/detailers'
app.config['CHECKPOINTS_DIR'] = 'data/checkpoints'
app.config['LOOKS_DIR'] = 'data/looks'
app.config['PRESETS_DIR'] = 'data/presets'
app.config['COMFYUI_URL'] = os.environ.get('COMFYUI_URL', 'http://127.0.0.1:8188')
app.config['ILLUSTRIOUS_MODELS_DIR'] = '/ImageModels/Stable-diffusion/Illustrious/'
app.config['NOOB_MODELS_DIR'] = '/ImageModels/Stable-diffusion/Noob/'
app.config['LORA_DIR'] = '/ImageModels/lora/Illustrious/Looks/'
# Server-side session configuration to avoid cookie size limits
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = os.path.join(app.config['UPLOAD_FOLDER'], '../flask_session')
app.config['SESSION_PERMANENT'] = False
db.init_app(app) db.init_app(app)
Session(app)
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}
# ---------------------------------------------------------------------------
def get_available_checkpoints(): # Logging
checkpoints = [] # ---------------------------------------------------------------------------
log_level_str = os.environ.get('LOG_LEVEL', 'INFO').upper()
# Scan Illustrious log_level = getattr(logging, log_level_str, logging.INFO)
if os.path.exists(app.config['ILLUSTRIOUS_MODELS_DIR']):
for f in os.listdir(app.config['ILLUSTRIOUS_MODELS_DIR']): logging.basicConfig(
if f.endswith('.safetensors') or f.endswith('.ckpt'): level=log_level,
checkpoints.append(f"Illustrious/{f}") format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
# Scan Noob )
if os.path.exists(app.config['NOOB_MODELS_DIR']): logger = logging.getLogger('gaze')
for f in os.listdir(app.config['NOOB_MODELS_DIR']): logger.setLevel(log_level)
if f.endswith('.safetensors') or f.endswith('.ckpt'):
checkpoints.append(f"Noob/{f}") # ---------------------------------------------------------------------------
# Register all routes
return sorted(checkpoints) # ---------------------------------------------------------------------------
from routes import register_routes
def allowed_file(filename): register_routes(app)
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
# ---------------------------------------------------------------------------
def build_prompt(data, selected_fields=None, default_fields=None): # Startup
def is_selected(section, key): # ---------------------------------------------------------------------------
# Priority:
# 1. Manual selection from form (if list is not empty)
# 2. Database defaults (if they exist)
# 3. Select all (default behavior)
if selected_fields:
return f"{section}::{key}" in selected_fields
if default_fields:
return f"{section}::{key}" in default_fields
return True
identity = data.get('identity', {})
wardrobe = data.get('wardrobe', {})
# Pre-calculate Hand/Glove priority
hand_val = ""
if wardrobe.get('gloves') and is_selected('wardrobe', 'gloves'):
hand_val = wardrobe.get('gloves')
elif identity.get('hands') and is_selected('identity', 'hands'):
hand_val = identity.get('hands')
# 1. Main Prompt
parts = ["(solo:1.2)"]
# Use character_id (underscores to spaces) for tags compatibility
char_tag = data.get('character_id', '').replace('_', ' ')
if char_tag and is_selected('special', 'name'):
parts.append(char_tag)
for key in ['base_specs', 'hair', 'eyes', 'expression', 'distinguishing_marks']:
val = identity.get(key)
if val and is_selected('identity', key):
parts.append(val)
# Add hand priority value to main prompt
if hand_val:
parts.append(hand_val)
for key in ['outer_layer', 'inner_layer', 'lower_body', 'footwear', 'accessories']:
val = wardrobe.get(key)
if val and is_selected('wardrobe', key):
parts.append(val)
style = data.get('styles', {}).get('aesthetic')
if style and is_selected('styles', 'aesthetic'):
parts.append(f"{style} style")
tags = data.get('tags', [])
if tags and is_selected('special', 'tags'):
parts.extend(tags)
lora = data.get('lora', {})
if lora.get('lora_triggers') and is_selected('lora', 'lora_triggers'):
parts.append(lora.get('lora_triggers'))
# 2. Face Prompt: Tag, Eyes, Expression
face_parts = []
if char_tag and is_selected('special', 'name'): face_parts.append(char_tag)
if identity.get('eyes') and is_selected('identity', 'eyes'): face_parts.append(identity.get('eyes'))
if identity.get('expression') and is_selected('identity', 'expression'): face_parts.append(identity.get('expression'))
# 3. Hand Prompt: Hand value (Gloves or Hands)
hand_parts = [hand_val] if hand_val else []
return {
"main": ", ".join(parts),
"face": ", ".join(face_parts),
"hand": ", ".join(hand_parts)
}
def queue_prompt(prompt_workflow, client_id=None):
p = {"prompt": prompt_workflow}
if client_id:
p["client_id"] = client_id
data = json.dumps(p).encode('utf-8')
response = requests.post(f"{app.config['COMFYUI_URL']}/prompt", data=data)
return response.json()
def get_history(prompt_id):
response = requests.get(f"{app.config['COMFYUI_URL']}/history/{prompt_id}")
return response.json()
def get_image(filename, subfolder, folder_type):
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
response = requests.get(f"{app.config['COMFYUI_URL']}/view", params=data)
return response.content
from sqlalchemy.orm.attributes import flag_modified
def sync_characters():
if not os.path.exists(app.config['CHARACTERS_DIR']):
return
current_ids = []
for filename in os.listdir(app.config['CHARACTERS_DIR']):
if filename.endswith('.json'):
file_path = os.path.join(app.config['CHARACTERS_DIR'], filename)
try:
with open(file_path, 'r') as f:
data = json.load(f)
char_id = data.get('character_id')
if not char_id:
continue
current_ids.append(char_id)
# Generate URL-safe slug: remove special characters from character_id
slug = re.sub(r'[^a-zA-Z0-9_]', '', char_id)
# Check if character already exists
character = Character.query.filter_by(character_id=char_id).first()
name = data.get('character_name', char_id.replace('_', ' ').title())
if character:
character.data = data
character.name = name
character.slug = slug
# Check if cover image still exists
if character.image_path:
full_img_path = os.path.join(app.config['UPLOAD_FOLDER'], character.image_path)
if not os.path.exists(full_img_path):
print(f"Image missing for {character.name}, clearing path.")
character.image_path = None
# Explicitly tell SQLAlchemy the JSON field was modified
flag_modified(character, "data")
else:
new_char = Character(
character_id=char_id,
slug=slug,
name=name,
data=data
)
db.session.add(new_char)
except Exception as e:
print(f"Error importing {filename}: {e}")
# Remove characters that are no longer in the folder
all_characters = Character.query.all()
for char in all_characters:
if char.character_id not in current_ids:
db.session.delete(char)
db.session.commit()
@app.route('/')
def index():
characters = Character.query.order_by(Character.name).all()
return render_template('index.html', characters=characters)
@app.route('/rescan', methods=['POST'])
def rescan():
sync_characters()
flash('Database synced with character files.')
return redirect(url_for('index'))
@app.route('/generator', methods=['GET', 'POST'])
def generator():
characters = Character.query.order_by(Character.name).all()
checkpoints = get_available_checkpoints()
if not checkpoints:
checkpoints = ["Noob/oneObsession_v19Atypical.safetensors"]
if request.method == 'POST':
char_slug = request.form.get('character')
checkpoint = request.form.get('checkpoint')
custom_positive = request.form.get('positive_prompt', '')
custom_negative = request.form.get('negative_prompt', '')
character = Character.query.filter_by(slug=char_slug).first_or_404()
try:
with open('comfy_workflow.json', 'r') as f:
workflow = json.load(f)
# Build base prompts from character defaults
prompts = build_prompt(character.data, default_fields=character.default_fields)
# Append custom additions to the "main" prompt
if custom_positive:
prompts["main"] = f"{prompts['main']}, {custom_positive}"
# Prepare workflow with custom checkpoint and negative prompt
workflow = _prepare_workflow(workflow, character, prompts, checkpoint, custom_negative)
print(f"Queueing generator prompt for {character.character_id}")
prompt_response = queue_prompt(workflow)
if 'prompt_id' not in prompt_response:
raise Exception(f"ComfyUI failed: {prompt_response.get('error', 'Unknown error')}")
prompt_id = prompt_response['prompt_id']
flash("Generation started...")
max_retries = 120
while max_retries > 0:
history = get_history(prompt_id)
if prompt_id in history:
outputs = history[prompt_id]['outputs']
for node_id in outputs:
if 'images' in outputs[node_id]:
image_info = outputs[node_id]['images'][0]
image_data = get_image(image_info['filename'], image_info['subfolder'], image_info['type'])
char_folder = os.path.join(app.config['UPLOAD_FOLDER'], character.slug)
os.makedirs(char_folder, exist_ok=True)
filename = f"gen_{int(time.time())}.png"
file_path = os.path.join(char_folder, filename)
with open(file_path, 'wb') as f:
f.write(image_data)
relative_path = f"{character.slug}/{filename}"
return render_template('generator.html', characters=characters, checkpoints=checkpoints,
generated_image=relative_path, selected_char=char_slug, selected_ckpt=checkpoint)
time.sleep(2)
max_retries -= 1
flash("Generation timed out.")
except Exception as e:
print(f"Generator error: {e}")
flash(f"Error: {str(e)}")
return render_template('generator.html', characters=characters, checkpoints=checkpoints)
@app.route('/character/<path:slug>')
def detail(slug):
character = Character.query.filter_by(slug=slug).first_or_404()
# Load state from session
preferences = session.get(f'prefs_{slug}')
preview_image = session.get(f'preview_{slug}')
return render_template('detail.html', character=character, preferences=preferences, preview_image=preview_image)
@app.route('/character/<path:slug>/upload', methods=['POST'])
def upload_image(slug):
character = Character.query.filter_by(slug=slug).first_or_404()
if 'image' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['image']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
# Create character subfolder
char_folder = os.path.join(app.config['UPLOAD_FOLDER'], slug)
os.makedirs(char_folder, exist_ok=True)
filename = secure_filename(file.filename)
file_path = os.path.join(char_folder, filename)
file.save(file_path)
# Store relative path in DB
character.image_path = f"{slug}/{filename}"
db.session.commit()
flash('Image uploaded successfully!')
return redirect(url_for('detail', slug=slug))
@app.route('/character/<path:slug>/finalize_generation/<prompt_id>', methods=['POST'])
def finalize_generation(slug, prompt_id):
character = Character.query.filter_by(slug=slug).first_or_404()
action = request.form.get('action', 'preview')
try:
history = get_history(prompt_id)
if prompt_id not in history:
return {'error': 'History not found'}, 404
outputs = history[prompt_id]['outputs']
for node_id in outputs:
if 'images' in outputs[node_id]:
image_info = outputs[node_id]['images'][0]
image_data = get_image(image_info['filename'], image_info['subfolder'], image_info['type'])
# Create character subfolder
char_folder = os.path.join(app.config['UPLOAD_FOLDER'], slug)
os.makedirs(char_folder, exist_ok=True)
filename = f"gen_{int(time.time())}.png"
file_path = os.path.join(char_folder, filename)
with open(file_path, 'wb') as f:
f.write(image_data)
print(f"Image saved to: {os.path.abspath(file_path)}")
# Handle actions
relative_path = f"{slug}/{filename}"
if action == 'replace':
character.image_path = relative_path
db.session.commit()
flash('Cover image updated!')
else:
# Preview mode
session[f'preview_{slug}'] = relative_path
return {'success': True, 'image_url': url_for('static', filename=f'uploads/{relative_path}')}
return {'error': 'No image found in output'}, 404
except Exception as e:
print(f"Finalize error: {e}")
return {'error': str(e)}, 500
def _prepare_workflow(workflow, character, prompts, checkpoint=None, custom_negative=None):
# 1. Update prompts using replacement to preserve embeddings
workflow["6"]["inputs"]["text"] = workflow["6"]["inputs"]["text"].replace("{{POSITIVE_PROMPT}}", prompts["main"])
if custom_negative:
workflow["7"]["inputs"]["text"] = f"{workflow['7']['inputs']['text']}, {custom_negative}"
if "14" in workflow:
workflow["14"]["inputs"]["text"] = workflow["14"]["inputs"]["text"].replace("{{FACE_PROMPT}}", prompts["face"])
if "15" in workflow:
workflow["15"]["inputs"]["text"] = workflow["15"]["inputs"]["text"].replace("{{HAND_PROMPT}}", prompts["hand"])
print("--- DEBUG: COMFYUI PROMPTS ---")
print(f"Main Positive (6): {workflow['6']['inputs']['text']}")
print(f"Main Negative (7): {workflow['7']['inputs']['text']}")
if "14" in workflow:
print(f"Face Detailer (14): {workflow['14']['inputs']['text']}")
if "15" in workflow:
print(f"Hand Detailer (15): {workflow['15']['inputs']['text']}")
print("-------------------------------")
# 2. Update Checkpoint
if checkpoint:
workflow["4"]["inputs"]["ckpt_name"] = checkpoint
# 3. Handle LoRA
lora_data = character.data.get('lora', {})
lora_name = lora_data.get('lora_name')
model_source = ["4", 0]
clip_source = ["4", 1]
if lora_name and "16" in workflow:
workflow["16"]["inputs"]["lora_name"] = lora_name
workflow["16"]["inputs"]["strength_model"] = lora_data.get('lora_weight', 1.0)
workflow["16"]["inputs"]["strength_clip"] = lora_data.get('lora_weight', 1.0)
model_source = ["16", 0]
clip_source = ["16", 1]
# Apply connections to all model/clip consumers
workflow["3"]["inputs"]["model"] = model_source
workflow["11"]["inputs"]["model"] = model_source
workflow["13"]["inputs"]["model"] = model_source
workflow["6"]["inputs"]["clip"] = clip_source
workflow["7"]["inputs"]["clip"] = clip_source
workflow["11"]["inputs"]["clip"] = clip_source
workflow["13"]["inputs"]["clip"] = clip_source
workflow["14"]["inputs"]["clip"] = clip_source
workflow["15"]["inputs"]["clip"] = clip_source
# 4. Randomize seeds
gen_seed = random.randint(1, 10**15)
workflow["3"]["inputs"]["seed"] = gen_seed
if "11" in workflow: workflow["11"]["inputs"]["seed"] = gen_seed
if "13" in workflow: workflow["13"]["inputs"]["seed"] = gen_seed
return workflow
def _queue_generation(character, action='preview', selected_fields=None, client_id=None):
# 1. Load workflow
with open('comfy_workflow.json', 'r') as f:
workflow = json.load(f)
# 2. Build prompts
prompts = build_prompt(character.data, selected_fields, character.default_fields)
# 3. Prepare workflow
workflow = _prepare_workflow(workflow, character, prompts)
return queue_prompt(workflow, client_id=client_id)
@app.route('/get_missing_characters')
def get_missing_characters():
missing = Character.query.filter((Character.image_path == None) | (Character.image_path == '')).all()
return {'missing': [{'slug': c.slug, 'name': c.name} for c in missing]}
@app.route('/clear_all_covers', methods=['POST'])
def clear_all_covers():
characters = Character.query.all()
for char in characters:
char.image_path = None
db.session.commit()
return {'success': True}
@app.route('/generate_missing', methods=['POST'])
def generate_missing():
missing = Character.query.filter((Character.image_path == None) | (Character.image_path == '')).all()
if not missing:
flash("No characters missing cover images.")
return redirect(url_for('index'))
success_count = 0
for character in missing:
try:
print(f"Batch generating for: {character.name}")
prompt_response = _queue_generation(character, action='replace')
prompt_id = prompt_response['prompt_id']
# Simple synchronous wait for each
max_retries = 120
while max_retries > 0:
history = get_history(prompt_id)
if prompt_id in history:
outputs = history[prompt_id]['outputs']
for node_id in outputs:
if 'images' in outputs[node_id]:
image_info = outputs[node_id]['images'][0]
image_data = get_image(image_info['filename'], image_info['subfolder'], image_info['type'])
char_folder = os.path.join(app.config['UPLOAD_FOLDER'], character.slug)
os.makedirs(char_folder, exist_ok=True)
filename = f"gen_{int(time.time())}.png"
file_path = os.path.join(char_folder, filename)
with open(file_path, 'wb') as f:
f.write(image_data)
character.image_path = f"{character.slug}/{filename}"
db.session.commit()
success_count += 1
break
break
time.sleep(2)
max_retries -= 1
except Exception as e:
print(f"Error generating for {character.name}: {e}")
flash(f"Batch generation complete. Generated {success_count} images.")
return redirect(url_for('index'))
@app.route('/check_status/<prompt_id>')
def check_status(prompt_id):
try:
history = get_history(prompt_id)
if prompt_id in history:
return {'status': 'finished'}
return {'status': 'pending'}
except Exception:
return {'status': 'error'}, 500
@app.route('/character/<path:slug>/generate', methods=['POST'])
def generate_image(slug):
character = Character.query.filter_by(slug=slug).first_or_404()
try:
# Get action type
action = request.form.get('action', 'preview')
client_id = request.form.get('client_id')
# Get selected fields
selected_fields = request.form.getlist('include_field')
# Save preferences
session[f'prefs_{slug}'] = selected_fields
# Queue generation using helper
prompt_response = _queue_generation(character, action, selected_fields, client_id=client_id)
if 'prompt_id' not in prompt_response:
raise Exception(f"ComfyUI failed: {prompt_response.get('error', 'Unknown error')}")
prompt_id = prompt_response['prompt_id']
# Return JSON if AJAX request
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return {'status': 'queued', 'prompt_id': prompt_id}
return redirect(url_for('detail', slug=slug))
except Exception as e:
print(f"Generation error: {e}")
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return {'error': str(e)}, 500
flash(f"Error during generation: {str(e)}")
return redirect(url_for('detail', slug=slug))
@app.route('/character/<path:slug>/save_defaults', methods=['POST'])
def save_defaults(slug):
character = Character.query.filter_by(slug=slug).first_or_404()
selected_fields = request.form.getlist('include_field')
character.default_fields = selected_fields
db.session.commit()
flash('Default prompt selection saved for this character!')
return redirect(url_for('detail', slug=slug))
if __name__ == '__main__': if __name__ == '__main__':
from services.mcp import ensure_mcp_server_running, ensure_character_mcp_server_running
from services.job_queue import init_queue_worker
from services.sync import (
sync_characters, sync_outfits, sync_actions, sync_styles,
sync_detailers, sync_scenes, sync_looks, sync_checkpoints, sync_presets,
)
ensure_mcp_server_running()
ensure_character_mcp_server_running()
init_queue_worker(app)
with app.app_context(): with app.app_context():
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
db.create_all() db.create_all()
# Migration: Add active_outfit column if it doesn't exist
try:
from sqlalchemy import text
db.session.execute(text('ALTER TABLE character ADD COLUMN active_outfit VARCHAR(100) DEFAULT \'default\''))
db.session.commit()
print("Added active_outfit column to character table")
except Exception as e:
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
print("active_outfit column already exists")
else:
print(f"Migration note: {e}")
# Migration: Add default_fields column to action table if it doesn't exist
try:
from sqlalchemy import text
db.session.execute(text('ALTER TABLE action ADD COLUMN default_fields JSON'))
db.session.commit()
print("Added default_fields column to action table")
except Exception as e:
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
print("default_fields column already exists in action table")
else:
print(f"Migration action note: {e}")
# Migration: Add new columns to settings table
columns_to_add = [
('llm_provider', "VARCHAR(50) DEFAULT 'openrouter'"),
('local_base_url', "VARCHAR(255)"),
('local_model', "VARCHAR(100)"),
('lora_dir_characters', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Looks'"),
('lora_dir_outfits', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Clothing'"),
('lora_dir_actions', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Poses'"),
('lora_dir_styles', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Styles'"),
('lora_dir_scenes', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Backgrounds'"),
('lora_dir_detailers', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Detailers'"),
('checkpoint_dirs', "VARCHAR(1000) DEFAULT '/ImageModels/Stable-diffusion/Illustrious,/ImageModels/Stable-diffusion/Noob'"),
('default_checkpoint', "VARCHAR(500)"),
('api_key', "VARCHAR(255)"),
]
for col_name, col_type in columns_to_add:
try:
db.session.execute(text(f'ALTER TABLE settings ADD COLUMN {col_name} {col_type}'))
db.session.commit()
print(f"Added {col_name} column to settings table")
except Exception as e:
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
pass
else:
print(f"Migration settings note ({col_name}): {e}")
# Migration: Add is_favourite and is_nsfw columns to all resource tables
_tag_tables = ['character', 'look', 'outfit', 'action', 'style', 'scene', 'detailer', 'checkpoint']
for _tbl in _tag_tables:
for _col, _type in [('is_favourite', 'BOOLEAN DEFAULT 0'), ('is_nsfw', 'BOOLEAN DEFAULT 0')]:
try:
db.session.execute(text(f'ALTER TABLE {_tbl} ADD COLUMN {_col} {_type}'))
db.session.commit()
print(f"Added {_col} column to {_tbl} table")
except Exception as e:
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
pass
else:
print(f"Migration note ({_tbl}.{_col}): {e}")
# Ensure settings exist
if not Settings.query.first():
db.session.add(Settings())
db.session.commit()
print("Created default settings")
# Log the default checkpoint on startup
settings = Settings.query.first()
if settings and settings.default_checkpoint:
logger.info("=" * 80)
logger.info("DEFAULT CHECKPOINT loaded from database: %s", settings.default_checkpoint)
logger.info("=" * 80)
else:
logger.info("No default checkpoint set in database")
sync_characters() sync_characters()
app.run(debug=True, port=5000) sync_outfits()
sync_actions()
# Migration: Add data column to checkpoint table
try:
db.session.execute(text('ALTER TABLE checkpoint ADD COLUMN data JSON'))
db.session.commit()
print("Added data column to checkpoint table")
except Exception as e:
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
print("data column already exists in checkpoint table")
else:
print(f"Migration checkpoint note: {e}")
sync_styles()
sync_detailers()
sync_scenes()
sync_looks()
sync_checkpoints()
sync_presets()
# Migration: Convert look.character_id to look.character_ids
try:
from sqlalchemy import text
# First ensure the column exists
db.session.execute(text("ALTER TABLE look ADD COLUMN character_ids JSON"))
db.session.commit()
print("Added character_ids column to look table")
except Exception as e:
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
pass # Column already exists
else:
print(f"Migration note (character_ids column): {e}")
# Migrate existing character_id to character_ids list
try:
looks_with_old_field = Look.query.filter(Look.character_id.isnot(None)).all()
migrated_count = 0
for look in looks_with_old_field:
if not look.character_ids:
look.character_ids = []
if look.character_id and look.character_id not in look.character_ids:
look.character_ids.append(look.character_id)
migrated_count += 1
if migrated_count > 0:
db.session.commit()
print(f"Migrated {migrated_count} looks from character_id to character_ids")
except Exception as e:
print(f"Migration note (character_ids data): {e}")
app.run(debug=True, host='0.0.0.0', port=5000)

View File

@@ -1,38 +0,0 @@
{
"character_id": "aerith_gainsborough",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "long brown hair, braided, pink ribbon",
"eyes": "green eyes",
"expression": "cheerful expression",
"hands": "pink nails",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "pink dress, red bolero jacket",
"lower_body": "long skirt",
"footwear": "brown boots",
"gloves": "",
"accessories": "gold bracelets, flower basket"
},
"styles": {
"aesthetic": "floral, gentle, final fantasy style",
"primary_color": "pink",
"secondary_color": "red",
"tertiary_color": "brown"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Final Fantasy VII"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "android_18",
"character_name": "Android 18",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "shoulder-length blonde hair, tucked behind one ear",
"eyes": "blue eyes",
"expression": "cool, indifferent expression",
"hands": "blue nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "gold hoop earrings"
},
"wardrobe": {
"inner_layer": "black short-sleeved shirt",
"outer_layer": "blue denim vest, 'RR' text on back",
"lower_body": "blue denim skirt, black leggings",
"footwear": "brown boots",
"gloves": "",
"accessories": ""
},
"styles": {
"aesthetic": "90s casual, anime, dragon ball style",
"primary_color": "blue",
"secondary_color": "black",
"tertiary_color": "white"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Dragon Ball Z"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "anya_(spy_x_family)",
"character_name": "Anya Forger",
"identity": {
"base_specs": "1girl, small build, loli, fair skin",
"hair": "short pink hair, two small horns (hair ornaments)",
"eyes": "green eyes",
"expression": "smirk",
"hands": "pink nails",
"arms": "",
"torso": "flat chest",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "black Eden Academy uniform, gold trim",
"lower_body": "uniform skirt",
"footwear": "black shoes, white socks",
"gloves": "",
"accessories": "black and gold hair cones"
},
"styles": {
"aesthetic": "cute, academic, spy x family style",
"primary_color": "pink",
"secondary_color": "black",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Spy x Family"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "biwa_hayahide_(Umamusume)",
"character_name": "Biwa Hayahide",
"identity": {
"base_specs": "1girl, horse ears, horse tail, tall",
"hair": "long grey hair, wild hair",
"eyes": "purple eyes, red framed glasses",
"expression": "thinking",
"hands": "",
"arms": "",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white shirt",
"outer_layer": "tracen school uniform",
"lower_body": "pleated skirt",
"footwear": "heeled shoes",
"gloves": "",
"accessories": ""
},
"styles": {
"aesthetic": "intellectual, cool",
"primary_color": "maroon",
"secondary_color": "white",
"tertiary_color": "grey"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Umamusume"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "bulma",
"character_name": "Bulma Briefs",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "turquoise hair, ponytail",
"eyes": "blue eyes",
"expression": "energetic smile",
"hands": "turquoise nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "black playboy bunny",
"lower_body": "pantyhose",
"footwear": "red high heels",
"gloves": "detatched cuffs",
"accessories": "red hair ribbon, dragon radar"
},
"styles": {
"aesthetic": "retro-futuristic, anime, dragon ball style",
"primary_color": "pink",
"secondary_color": "turquoise",
"tertiary_color": "purple"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Dragon Ball"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "camilla_(fire_emblem)",
"character_name": "Camilla Nohr",
"identity": {
"base_specs": "1girl, curvaceous build, fair skin",
"hair": "long wavy lavender hair, hair covering one eye",
"eyes": "purple eyes",
"expression": "seductive smile",
"hands": "purple nails",
"arms": "",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "black headband with horns"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "black armor, cleavage",
"lower_body": "black leggings, armored plates",
"footwear": "black armored boots",
"gloves": "",
"accessories": "purple cape, large axe"
},
"styles": {
"aesthetic": "dark fantasy, gothic, fire emblem style",
"primary_color": "black",
"secondary_color": "gold",
"tertiary_color": "purple"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Fire Emblem"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "cammy",
"character_name": "Cammy White",
"identity": {
"base_specs": "1girl, muscular build, fair skin",
"hair": "long blonde hair, twin braids",
"eyes": "blue eyes",
"expression": "serious look",
"hands": "green nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "scar on left cheek, green camouflage paint on legs"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "green high-leg leotard",
"lower_body": "bare legs",
"footwear": "black combat boots, green socks",
"gloves": "red gauntlets",
"accessories": "red beret"
},
"styles": {
"aesthetic": "military, athletic, street fighter style",
"primary_color": "green",
"secondary_color": "red",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Street Fighter"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "chun_li",
"character_name": "Chun-Li",
"identity": {
"base_specs": "1girl, muscular build, fair skin, asian",
"hair": "black hair, hair buns",
"eyes": "brown eyes",
"expression": "determined smile",
"hands": "blue nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "thick thighs",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "blue qipao, gold embroidery, white accents",
"lower_body": "brown tights",
"footwear": "white combat boots",
"gloves": "",
"accessories": "white hair ribbons, spiked bracelets"
},
"styles": {
"aesthetic": "chinese style",
"primary_color": "blue",
"secondary_color": "white",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Street Fighter"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "ciri",
"character_name": "Ciri",
"identity": {
"base_specs": "1girl, athletic build",
"hair": "ashen grey hair, messy bun",
"eyes": "emerald green eyes, mascara",
"expression": "determined look",
"hands": "green nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "scar over eye"
},
"wardrobe": {
"inner_layer": "white blouse",
"outer_layer": "",
"lower_body": "brown leather trousers",
"footwear": "brown leather boots",
"gloves": "brown leather gloves",
"accessories": "silver sword on back, witcher medallion"
},
"styles": {
"aesthetic": "gritty, fantasy, witcher style",
"primary_color": "white",
"secondary_color": "brown",
"tertiary_color": "silver"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"The Witcher 3"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "delinquent_mother_flim13",
"character_name": "Delinquent Mother",
"identity": {
"base_specs": "1girl, milf, gyaru, tall",
"hair": "blonde hair, long hair",
"eyes": "sharp eyes",
"expression": "smirk, sharp teeth",
"hands": "painted nails",
"arms": "",
"torso": "very large breasts",
"pelvis": "wide hips",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "biege sweater, cleavage",
"outer_layer": "",
"lower_body": "pencil skirt",
"footwear": "high heels",
"gloves": "",
"accessories": "necklace, rings"
},
"styles": {
"aesthetic": "gyaru, milf, pink leopard print",
"primary_color": "pink",
"secondary_color": "black",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "Illustrious/Looks/Gyaru_mom_Flim13_IL_V1.safetensors",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Original","flim13"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "gold_city_(Umamusume)",
"character_name": "Gold City",
"identity": {
"base_specs": "1girl, horse ears, horse tail, tall",
"hair": "blonde hair, wavy hair",
"eyes": "blue eyes",
"expression": "confident expression",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white shirt",
"outer_layer": "tracen school uniform",
"lower_body": "pleated skirt",
"footwear": "heeled shoes",
"gloves": "",
"accessories": "choker, earrings"
},
"styles": {
"aesthetic": "fashionable, model",
"primary_color": "gold",
"secondary_color": "white",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Umamusume"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "gold_ship_(Umamusume)",
"character_name": "Gold Ship",
"identity": {
"base_specs": "1girl, horse ears, horse tail, tall",
"hair": "grey hair, short hair",
"eyes": "red eyes",
"expression": "crazy expression, grin",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white shirt",
"outer_layer": "tracen school uniform",
"lower_body": "pleated skirt",
"footwear": "heeled shoes",
"gloves": "",
"accessories": "ear covers, hat"
},
"styles": {
"aesthetic": "energetic, sporty",
"primary_color": "red",
"secondary_color": "white",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Umamusume"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "hatsune_miku",
"character_name": "Hatsune Miku",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "long turquoise hair, twin tails, floor-length",
"eyes": "turquoise eyes",
"expression": "cheerful smile",
"hands": "turquoise nails",
"arms": "01 tattoo on left shoulder",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "grey sleeveless shirt, turquoise tie",
"lower_body": "grey miniskirt, turquoise trim",
"footwear": "black thigh-high boots, turquoise trim",
"gloves": "black arm warmers, turquoise trim",
"accessories": "hair ornament, headset"
},
"styles": {
"aesthetic": "vocaloid, futuristic, anime style",
"primary_color": "teal",
"secondary_color": "grey",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Vocaloid"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "jessica_rabbit",
"character_name": "Jessica Rabbit",
"identity": {
"base_specs": "1girl, voluptuous build, tall,",
"hair": "long red hair, side part, hair over one eye",
"eyes": "green eyes, heavy makeup, purple eyeshadow",
"expression": "seductive smile",
"hands": "purple elbow gloves",
"arms": "",
"torso": "large breasts",
"pelvis": "narrow waist",
"legs": "",
"feet": "",
"distinguishing_marks": "red lips"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "red sequin dress, strapless, high slit, backless",
"lower_body": "side_slit,",
"footwear": "red high heels",
"gloves": "purple opera gloves",
"accessories": "gold earrings, glitter"
},
"styles": {
"aesthetic": "noir, cartoon, glamorous",
"primary_color": "red",
"secondary_color": "purple",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 0.8,
"lora_triggers": ""
},
"tags": [
"Who Framed Roger Rabbit"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "jessie_(pokemon)",
"character_name": "Jessie",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "long magenta hair, curved back",
"eyes": "blue eyes",
"expression": "arrogant smirk",
"hands": "white nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "green earrings"
},
"wardrobe": {
"inner_layer": "black crop top",
"outer_layer": "white Team Rocket uniform jacket, bare stomach, red R logo",
"lower_body": "white miniskirt",
"footwear": "black thigh-high boots",
"gloves": "black elbow gloves",
"accessories": "green earrings"
},
"styles": {
"aesthetic": "villainous, anime, pokemon style",
"primary_color": "white",
"secondary_color": "magenta",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Pokemon"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "jinx_(league_of_legends)",
"character_name": "Jinx",
"identity": {
"base_specs": "1girl, slender build, pale skin,",
"hair": "long aqua hair, twin braids, very long hair, bangs",
"eyes": "pink eyes, ",
"expression": "crazy eyes, crazy smile",
"hands": "black and pink nails",
"arms": "",
"torso": "flat chest,",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "cloud tattoo,"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "pink and black bikini, asymmetrical_bikini ",
"lower_body": "pink shorts, single pink stocking",
"footwear": "combat boots",
"gloves": "black fingerless gloves, fishnet elbow gloves,",
"accessories": "ammo belts, choker, bullet necklace,"
},
"styles": {
"aesthetic": "punk, chaotic,",
"primary_color": "pink",
"secondary_color": "black",
"tertiary_color": "aqua"
},
"lora": {
"lora_name": "Illustrious/Looks/jinx_default_lol-000021.safetensors",
"lora_weight": 0.8,
"lora_triggers": ""
},
"tags": [
"League of Legends"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "kagamine_rin",
"character_name": "Kagamine Rin",
"identity": {
"base_specs": "1girl, petite",
"hair": "blonde hair, short hair, hair bow",
"eyes": "blue eyes",
"expression": "smile, energetic",
"hands": "",
"arms": "detached sleeves",
"torso": "flat chest",
"pelvis": "",
"legs": "leg warmers",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white shirt, sailor collar",
"outer_layer": "",
"lower_body": "black shorts, yellow belt",
"footwear": "white shoes",
"gloves": "",
"accessories": "headset, hair bow"
},
"styles": {
"aesthetic": "vocaloid, cyber",
"primary_color": "yellow",
"secondary_color": "white",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Vocaloid"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "kagari_atsuko",
"character_name": "Kagari Atsuko",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "long brown hair, half-ponytail, bangs",
"eyes": "red eyes",
"expression": "determined smile",
"hands": "",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white shirt",
"outer_layer": "dark blue witch robes",
"lower_body": "dark blue skirt",
"footwear": "brown boots, white socks",
"gloves": "",
"accessories": "pointed witch hat, brown belt, magic wand"
},
"styles": {
"aesthetic": "fantasy, magical girl, little witch academia style",
"primary_color": "dark blue",
"secondary_color": "brown",
"tertiary_color": "red"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Little Witch Academia"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "k/da_all_out_ahri",
"character_name": "Ahri",
"identity": {
"base_specs": "1girl, slender build, fair skin, fox ears",
"hair": "long blonde hair, flowing",
"eyes": "yellow eyes",
"expression": "charming smile",
"hands": "silver nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "whisker markings on cheeks, crystal tails"
},
"wardrobe": {
"inner_layer": "silver crop top",
"outer_layer": "white and silver jacket",
"lower_body": "black leather shorts",
"footwear": "black thigh-high boots",
"gloves": "",
"accessories": "crystal heart, silver jewelry"
},
"styles": {
"aesthetic": "pop star, mystical, k/da style",
"primary_color": "silver",
"secondary_color": "white",
"tertiary_color": "blue"
},
"lora": {
"lora_name": "Illustrious/Looks/KDA AhriIlluLoRA.safetensors",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"League of Legends", "K/DA", "KDA", "K-Pop"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "k/da_all_out_akali",
"character_name": "Akali",
"identity": {
"base_specs": "1girl, athletic build, fair skin",
"hair": "long dark blue hair, blonde streaks, high ponytail",
"eyes": "blue eyes",
"expression": "cool, rebellious look",
"hands": "blue nails",
"arms": "tattoos on arms",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "black crop top",
"outer_layer": "blue and silver motorcycle jacket",
"lower_body": "black leather pants",
"footwear": "blue sneakers",
"gloves": "black fingerless gloves",
"accessories": "kama and kunai"
},
"styles": {
"aesthetic": "pop star, street, k/da style",
"primary_color": "blue",
"secondary_color": "purple",
"tertiary_color": "silver"
},
"lora": {
"lora_name": "Illustrious/Looks/KDAAkaliIlluLoRA.safetensors",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"League of Legends", "K/DA", "KDA", "K-Pop"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "k/da_all_out_evelynn",
"character_name": "Evelynn",
"identity": {
"base_specs": "1girl, curvaceous build, fair skin",
"hair": "light blue hair,",
"eyes": "yellow glowing eyes, slit pupils",
"expression": "seductive, confident look",
"hands": "metal claws",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "two long lashers (shadow tendrils)"
},
"wardrobe": {
"inner_layer": "black leather bra",
"outer_layer": "iridescent blue jacket, fur collar",
"lower_body": "black leather skirt",
"footwear": "black high-heeled boots",
"gloves": "",
"accessories": "diamond earrings"
},
"styles": {
"aesthetic": "pop star, glamorous, k/da style",
"primary_color": "blue",
"secondary_color": "purple",
"tertiary_color": "silver"
},
"lora": {
"lora_name": "Illustrious/Looks/KDA EvelynnIlluLoRA.safetensors",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"League of Legends", "K/DA", "KDA", "K-Pop"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "k/da_all_out_kai'sa",
"character_name": "Kai'Sa",
"identity": {
"base_specs": "1girl, athletic build, fair skin",
"hair": "long hair, purple hair, hair ornament, ponytail, green highlights",
"eyes": "purple eyes",
"expression": "focused expression",
"hands": "silver nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "silver bodysuit",
"outer_layer": "white and silver jacket",
"lower_body": "silver leggings",
"footwear": "silver high-heeled boots",
"gloves": "",
"accessories": "crystal shoulder pods"
},
"styles": {
"aesthetic": "pop star, futuristic, k/da style",
"primary_color": "silver",
"secondary_color": "white",
"tertiary_color": "purple"
},
"lora": {
"lora_name": "Illustrious/Looks/KDA KaisaIlluLoRA.safetensors",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"League of Legends", "K/DA", "KDA", "K-Pop"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "komi_shouko",
"character_name": "Komi Shouko",
"identity": {
"base_specs": "1girl, slender build, pale skin, asian",
"hair": "long dark purple hair, hime cut,",
"eyes": "dark purple eyes,",
"expression": "neutral expression, stoic, cat ears",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "black pantyhose",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white shirt",
"outer_layer": "itan private high school uniform, blazer, striped bow tie",
"lower_body": "plaid skirt",
"footwear": "loafers",
"gloves": "",
"accessories": ""
},
"styles": {
"aesthetic": "anime, manga, clean lines",
"primary_color": "purple",
"secondary_color": "magenta",
"tertiary_color": "white"
},
"lora": {
"lora_name": "",
"lora_weight": 0.8,
"lora_triggers": "komi shouko, itan private high school uniform"
},
"tags": [
"Komi Can't Communicate"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "lara_croft_classic",
"character_name": "Lara Croft",
"identity": {
"base_specs": "1girl, athletic build,",
"hair": "long brown hair, single braid",
"eyes": "brown eyes",
"expression": "light smile, raised eyebrow",
"hands": "",
"arms": "",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "teal tank top,",
"lower_body": "brown shorts",
"footwear": "brown combat boots, red laces",
"gloves": "black fingerless gloves",
"accessories": "dual thigh pistol holsters, brown leatherbackpack, red circular sunglasses"
},
"styles": {
"aesthetic": "adventure, retro, 90s style",
"primary_color": "teal",
"secondary_color": "brown",
"tertiary_color": "black"
},
"lora": {
"lora_name": "Illustrious/Looks/LaraCroft_ClassicV2_Illu_Dwnsty.safetensors",
"lora_weight": 0.8,
"lora_triggers": ""
},
"tags": [
"Tomb Raider"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "lisa_(genshin_impact)",
"character_name": "Lisa Minci",
"identity": {
"base_specs": "1girl, tall, mature female",
"hair": "brown hair, wavy hair, side ponytail",
"eyes": "green eyes",
"expression": "seductive smile",
"hands": "",
"arms": "detached sleeves",
"torso": "large breasts",
"pelvis": "wide hips",
"legs": "black pantyhose",
"feet": "",
"distinguishing_marks": "beauty mark"
},
"wardrobe": {
"inner_layer": "purple dress, corset",
"outer_layer": "purple shawl",
"lower_body": "slit skirt",
"footwear": "black heels",
"gloves": "purple gloves",
"accessories": "witch hat, rose, necklace"
},
"styles": {
"aesthetic": "genshin impact, witch, librarian",
"primary_color": "purple",
"secondary_color": "white",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Genshin Impact"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "lulu (ff10)",
"character_name": "Lulu",
"identity": {
"base_specs": "1girl, curvaceous build, fair skin",
"hair": "long black hair, complex braids, hairpins",
"eyes": "red eyes",
"expression": "thinking, raised eyebrow",
"hands": "black nails",
"arms": "",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "dark purple lipstick"
},
"wardrobe": {
"inner_layer": "black corset",
"outer_layer": "black fur-trimmed dress, many belts on front",
"lower_body": "long skirt made of belts",
"footwear": "black boots",
"gloves": "",
"accessories": "moogle doll, silver jewelry"
},
"styles": {
"aesthetic": "gothic, ornate, final fantasy x style",
"primary_color": "black",
"secondary_color": "white",
"tertiary_color": "purple"
},
"lora": {
"lora_name": "Illustrious/Looks/Lulu DG illuLoRA_1337272.safetensors",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Final Fantasy X"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "majin_android_21",
"character_name": "Majin Android 21",
"identity": {
"base_specs": "1girl, curvaceous build, pink skin",
"hair": "long voluminous white hair",
"eyes": "red eyes, black sclera",
"expression": "evil smile",
"hands": "black claws, pink nails",
"arms": "",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "pink skin, long tail, pointy ears"
},
"wardrobe": {
"inner_layer": "black tube top",
"outer_layer": "",
"lower_body": "white harem pants",
"footwear": "black and yellow boots",
"gloves": "black sleeves",
"accessories": "gold bracelets, gold neck ring, hoop earrings"
},
"styles": {
"aesthetic": "supernatural, anime, dragon ball style",
"primary_color": "pink",
"secondary_color": "white",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Dragon Ball FighterZ"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "marin_kitagawa",
"character_name": "Marin Kitagawa",
"identity": {
"base_specs": "1girl, slender build, fair skin, asian",
"hair": "long blonde hair, pink tips",
"eyes": "pink eyes (contacts)",
"expression": "excited smile",
"hands": "long pink nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "piercings"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "white school shirt, loosely tied blue tie",
"lower_body": "blue plaid miniskirt",
"footwear": "black loafers, black socks",
"gloves": "",
"accessories": "choker, various bracelets"
},
"styles": {
"aesthetic": "gyaru, modern, anime style",
"primary_color": "white",
"secondary_color": "blue",
"tertiary_color": "pink"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"My Dress-Up Darling"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "megurine_luka",
"character_name": "Megurine Luka",
"identity": {
"base_specs": "1girl, tall, mature female",
"hair": "pink hair, long hair",
"eyes": "blue eyes",
"expression": "light smile",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "crop top, detached sleeves, gold trim",
"lower_body": "side slit, lace-up skirt",
"footwear": "thinghighs, lace-up boots, gold boots, gold armlet",
"gloves": "",
"accessories": "headset"
},
"styles": {
"aesthetic": "vocaloid, elegant",
"primary_color": "black",
"secondary_color": "gold",
"tertiary_color": "pink"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Vocaloid"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "meiko",
"character_name": "Meiko",
"identity": {
"base_specs": "1girl, mature female",
"hair": "brown hair, short hair",
"eyes": "brown eyes",
"expression": "smile, confident",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "red crop top, sleeveless",
"outer_layer": "",
"lower_body": "red skirt, mini skirt",
"footwear": "brown boots",
"gloves": "",
"accessories": "choker"
},
"styles": {
"aesthetic": "vocaloid, casual",
"primary_color": "red",
"secondary_color": "brown",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Vocaloid"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "nessa",
"character_name": "Nessa",
"identity": {
"base_specs": "1girl, athletic build, dark skin",
"hair": "long hair, light blue highlights",
"eyes": "blue eyes",
"expression": "confident smile",
"hands": "blue nails",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "blue earrings"
},
"wardrobe": {
"inner_layer": "white and blue bikini top",
"outer_layer": "gym uniform, number 049",
"lower_body": "white and blue shorts",
"footwear": "blue and white sandals",
"gloves": "",
"accessories": "wristband, life buoy, pokeball"
},
"styles": {
"aesthetic": "sporty, aquatic, pokemon style",
"primary_color": "blue",
"secondary_color": "white",
"tertiary_color": "orange"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Pokemon"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "olivier_mira_armstrong",
"character_name": "Olivier Mira Armstrong",
"identity": {
"base_specs": "1girl, tall, mature female",
"hair": "blonde hair, long hair, hair over one eye",
"eyes": "blue eyes, sharp eyes",
"expression": "serious",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "thick lips"
},
"wardrobe": {
"inner_layer": "black shirt",
"outer_layer": "blue military coat, fur collar",
"lower_body": "black pants",
"footwear": "black boots",
"gloves": "black gloves",
"accessories": "sword"
},
"styles": {
"aesthetic": "military, amestris uniform",
"primary_color": "blue",
"secondary_color": "black",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Fullmetal Alchemist"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "princess_peach",
"character_name": "Princess Peach",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "long blonde hair, voluminous, crown",
"eyes": "blue eyes, long eyelashes",
"expression": "gentle smile",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "pink lips, blue earrings"
},
"wardrobe": {
"inner_layer": "white petticoat",
"outer_layer": "pink floor-length ball gown, puffy sleeves, dark pink panniers",
"lower_body": "long skirt",
"footwear": "red high heels",
"gloves": "white opera gloves",
"accessories": "gold crown with red and blue jewels, blue brooch"
},
"styles": {
"aesthetic": "royal, whimsical, nintendo style",
"primary_color": "pink",
"secondary_color": "gold",
"tertiary_color": "blue"
},
"lora": {
"lora_name": "Illustrious/Looks/Princess_Peach_Shiny_Style_V4.0_Illustrious_1652958.safetensors",
"lora_weight": 0.8,
"lora_triggers": "princess peach, crown, pink dress, shiny skin, royal elegance"
},
"tags": [
"Super Mario"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "princess_zelda_botw",
"character_name": "Princess Zelda",
"identity": {
"base_specs": "1girl, slender build, fair skin, pointed ears",
"hair": "long blonde hair, braided, gold hair clips",
"eyes": "green eyes",
"expression": "curious",
"hands": "gold nails",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "tri-force symbol, elf ears"
},
"wardrobe": {
"inner_layer": "blue tunic",
"outer_layer": "blue champion's tunic, brown leather belts",
"lower_body": "tan trousers",
"footwear": "brown leather boots",
"gloves": "brown fingerless gloves",
"accessories": "sheikah slate, gold jewelry"
},
"styles": {
"aesthetic": "fantasy, adventurous, zelda style",
"primary_color": "blue",
"secondary_color": "gold",
"tertiary_color": "brown"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"The Legend of Zelda"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "rice_shower_(Umamusume)",
"character_name": "Rice Shower",
"identity": {
"base_specs": "1girl, petite, horse ears, horse tail",
"hair": "long dark brown hair, bangs, hair over one eye",
"eyes": "purple eyes",
"expression": "shy expression",
"hands": "",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white shirt",
"outer_layer": "tracen school uniform",
"lower_body": "pleated skirt",
"footwear": "heeled shoes",
"gloves": "",
"accessories": "blue rose, hair flower, small hat, dagger"
},
"styles": {
"aesthetic": "gothic lolita, elegant",
"primary_color": "purple",
"secondary_color": "blue",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Umamusume"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "riju",
"character_name": "Riju",
"identity": {
"base_specs": "1girl, young, dark skin, gerudo",
"hair": "short red hair, braided ponytail, gold hair ornament",
"eyes": "green eyes",
"expression": "serious",
"hands": "",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "darkblue lipstick,"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "black top, blue sash",
"lower_body": "black skirt, pelvic curtain,",
"footwear": "gold high heels",
"gloves": "",
"accessories": "gold jewelry, earrings"
},
"styles": {
"aesthetic": "fantasy, desert, gerudo style",
"primary_color": "gold",
"secondary_color": "black",
"tertiary_color": "red"
},
"lora": {
"lora_name": "",
"lora_weight": 0.8,
"lora_triggers": ""
},
"tags": [
"The Legend of Zelda"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "rosalina",
"character_name": "Rosalina",
"identity": {
"base_specs": "1girl, tall, slender build, fair skin",
"hair": "long platinum blonde hair, side-swept bangs covering one eye",
"eyes": "light blue eyes",
"expression": "serene expression",
"hands": "turquoise nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "star-shaped earrings"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "turquoise off-the-shoulder gown, silver trim",
"lower_body": "long skirt",
"footwear": "silver high heels",
"gloves": "",
"accessories": "silver crown with blue jewels, star wand, luma"
},
"styles": {
"aesthetic": "celestial, elegant, nintendo style",
"primary_color": "turquoise",
"secondary_color": "silver",
"tertiary_color": "yellow"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Super Mario"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "rouge_the_bat",
"character_name": "Rouge the Bat",
"identity": {
"base_specs": "1girl, anthro, bat girl, white fur",
"hair": "short white hair",
"eyes": "teal eyes",
"expression": "sly smirk",
"hands": "white gloves",
"arms": "",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "bat wings, eyeshadow"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "black skin-tight jumpsuit, pink heart-shaped chest plate, bare shoulders, cleavage",
"lower_body": "jumpsuit",
"footwear": "white boots, pink heart motifs",
"gloves": "white gloves, pink cuffs",
"accessories": "blue eyeshadow"
},
"styles": {
"aesthetic": "jewels, museum,sleek, spy, sonic style",
"primary_color": "white",
"secondary_color": "pink",
"tertiary_color": "black"
},
"lora": {
"lora_name": "Illustrious/Looks/Rouge_the_bat_v2.safetensors",
"lora_weight": 0.8,
"lora_triggers": ""
},
"tags": [
"Sonic the Hedgehog"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "ryouko_(tenchi_muyou!)",
"character_name": "Ryouko Hakubi",
"identity": {
"base_specs": "1girl, slim build,",
"hair": "long teal hair, spiky, voluminous",
"eyes": "golden eyes, cat-like pupils",
"expression": "confident smirk",
"hands": "",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "red gem on forehead,"
},
"wardrobe": {
"inner_layer": "long white dress, plunging neckline, black belt",
"outer_layer": "black and orange long sleeve jacket with purple trim,",
"lower_body": "side_slit,, red trousers",
"footwear": "",
"gloves": "red gloves",
"accessories": "red gems, wristbands"
},
"styles": {
"aesthetic": "90s anime, sci-fi",
"primary_color": "teal",
"secondary_color": "white",
"tertiary_color": "red"
},
"lora": {
"lora_name": "",
"lora_weight": 0.8,
"lora_triggers": "ryouko hakubi, space pirate"
},
"tags": [
"Tenchi Muyou!", "Tenchi Muyo!"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "samus_aran",
"character_name": "Samus Aran",
"identity": {
"base_specs": "1girl, athletic build, fair skin",
"hair": "long blonde hair, ponytail",
"eyes": "blue eyes",
"expression": "serious expression",
"hands": "blue nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "beauty mark on chin"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "blue skin-tight bodysuit, pink symbols",
"lower_body": "bodysuit",
"footwear": "blue high-heeled boots",
"gloves": "zero suit",
"accessories": "paralyzer pistol"
},
"styles": {
"aesthetic": "sci-fi, sleek, metroid style",
"primary_color": "blue",
"secondary_color": "pink",
"tertiary_color": "yellow"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Metroid"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "sarah_miller_(the_last_of_us)",
"character_name": "Sarah Miller",
"identity": {
"base_specs": "1girl, loli, small build",
"hair": "blonde hair, short hair",
"eyes": "blue eyes",
"expression": "smile",
"hands": "",
"arms": "",
"torso": "flat chest",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "grey t-shirt, white shirt",
"outer_layer": "",
"lower_body": "blue jeans",
"footwear": "sneakers",
"gloves": "",
"accessories": "wristwatch"
},
"styles": {
"aesthetic": "casual, 2013 fashion",
"primary_color": "grey",
"secondary_color": "blue",
"tertiary_color": "white"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"The Last of Us"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "shantae",
"character_name": "Shantae",
"identity": {
"base_specs": "1girl, dark skin, pointy ears",
"hair": "purple hair, very long hair, ponytail",
"eyes": "blue eyes",
"expression": "smile, energetic",
"hands": "",
"arms": "gold bracelets",
"torso": "small breasts, perky breasts",
"pelvis": "wide hips",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "red bikini top, red harem pants, gold trim",
"lower_body": "",
"footwear": "gold shoes",
"gloves": "",
"accessories": "gold tiara, hoop earrings"
},
"styles": {
"aesthetic": "genie, dancer, arabian",
"primary_color": "red",
"secondary_color": "gold",
"tertiary_color": "purple"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Shantae"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "sucy_manbavaran",
"character_name": "Sucy Manbavaran",
"identity": {
"base_specs": "1girl, lanky build, pale skin",
"hair": "light purple hair, hair covering one eye",
"eyes": "red eyes",
"expression": "deadpan expression",
"hands": "black nails",
"arms": "",
"torso": "small breasts",
"pelvis": "narrow waist",
"legs": "",
"feet": "",
"distinguishing_marks": "dark circles under eyes"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "dark purple witch robes",
"lower_body": "long skirt with frayed edges",
"footwear": "brown boots",
"gloves": "",
"accessories": "pointed witch hat, potion bottle"
},
"styles": {
"aesthetic": "gothic, whimsical, little witch academia style",
"primary_color": "purple",
"secondary_color": "mauve",
"tertiary_color": "green"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Little Witch Academia"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "tifa_lockhart",
"character_name": "Tifa Lockhart",
"identity": {
"base_specs": "1girl, athletic build, fair skin",
"hair": "long black hair, tied end",
"eyes": "red eyes",
"expression": "kind smile",
"hands": "dark red nails",
"arms": "",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "black sports bra",
"outer_layer": "white tank top, black suspenders",
"lower_body": "black miniskirt",
"footwear": "red boots, black socks",
"gloves": "red fingerless gloves",
"accessories": "silver earrings"
},
"styles": {
"aesthetic": "urban, martial arts, final fantasy style",
"primary_color": "white",
"secondary_color": "black",
"tertiary_color": "red"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Final Fantasy VII"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "tracer",
"character_name": "Tracer",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "short spiky brown hair",
"eyes": "brown eyes",
"expression": "energetic smile",
"hands": "",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "freckles"
},
"wardrobe": {
"inner_layer": "orange leggings",
"outer_layer": "brown flight jacket, yellow vest",
"lower_body": "orange leggings",
"footwear": "white and orange sneakers",
"gloves": "",
"accessories": "chronal accelerator, yellow goggles"
},
"styles": {
"aesthetic": "sci-fi, pilot, overwatch style",
"primary_color": "orange",
"secondary_color": "brown",
"tertiary_color": "white"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Overwatch"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "urbosa",
"character_name": "Urbosa",
"identity": {
"base_specs": "1girl, tall, muscular, dark skin, gerudo",
"hair": "long red hair, wild hair",
"eyes": "green eyes",
"expression": "confident",
"hands": "gold nails",
"arms": "muscular arms",
"torso": "abs, mediumS breasts",
"pelvis": "wide hips",
"legs": "muscular legs",
"feet": "",
"distinguishing_marks": "dark blue lipstick, gerudo markings"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "blue top, blue champion's skirt, green sash, green shoulder guards,",
"lower_body": "blue skirt",
"footwear": "gold heels",
"gloves": "",
"accessories": "gold jewelry, scimitar"
},
"styles": {
"aesthetic": "fantasy, warrior, gerudo style",
"primary_color": "gold",
"secondary_color": "blue",
"tertiary_color": "red"
},
"lora": {
"lora_name": "",
"lora_weight": 0.8,
"lora_triggers": ""
},
"tags": [
"The Legend of Zelda"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "widowmaker",
"character_name": "Widowmaker",
"identity": {
"base_specs": "1girl, slender build, blue skin",
"hair": "long purple hair, ponytail",
"eyes": "yellow eyes",
"expression": "cold expression",
"hands": "",
"arms": "spider tattoo on arm",
"torso": "large breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "blue skin"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "purple tactical bodysuit, plunging neckline",
"lower_body": "bodysuit",
"footwear": "purple high-heeled boots",
"gloves": "purple gauntlets",
"accessories": "sniper visor, grappling hook"
},
"styles": {
"aesthetic": "sci-fi, assassin, overwatch style",
"primary_color": "purple",
"secondary_color": "black",
"tertiary_color": "pink"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Overwatch"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "yor_briar",
"character_name": "Yor Briar",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "long black hair, styled with gold headband",
"eyes": "red eyes",
"expression": "gentle yet mysterious smile",
"hands": "black nails",
"arms": "",
"torso": "medium breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "black backless halter dress, red rose pattern inside",
"lower_body": "black thigh-high boots",
"footwear": "black boots",
"gloves": "black fingerless gloves",
"accessories": "gold rose-themed headband, gold needle weapons"
},
"styles": {
"aesthetic": "elegant, assassin, spy x family style",
"primary_color": "black",
"secondary_color": "red",
"tertiary_color": "gold"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Spy x Family"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "y'shtola_rhul",
"character_name": "Y'shtola Rhul",
"identity": {
"base_specs": "1girl, miqo'te, slender build, fair skin, cat ears",
"hair": "short white hair, bangs",
"eyes": "blind, white eyes",
"expression": "stoic expression",
"hands": "black nails",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "facial markings, cat tail"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "black sorceress robes, fur trim",
"lower_body": "long skirt",
"footwear": "black boots",
"gloves": "",
"accessories": "wooden staff"
},
"styles": {
"aesthetic": "magical, scholarly, final fantasy xiv style",
"primary_color": "black",
"secondary_color": "white",
"tertiary_color": "purple"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Final Fantasy XIV"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "yuffie_kisaragi",
"character_name": "Yuffie Kisaragi",
"identity": {
"base_specs": "1girl, slender build, fair skin",
"hair": "short black hair, bob cut",
"eyes": "brown eyes",
"expression": "playful grin",
"hands": "",
"arms": "black sleeve on one arm",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": "headband"
},
"wardrobe": {
"inner_layer": "",
"outer_layer": "green turtleneck sweater vest, midriff",
"lower_body": "beige shorts",
"footwear": "boots, socks",
"gloves": "fingerless glove on one hand, large gauntlet on one arm",
"accessories": "shuriken"
},
"styles": {
"aesthetic": "ninja, adventurer, final fantasy style",
"primary_color": "green",
"secondary_color": "beige",
"tertiary_color": "black"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": ""
},
"tags": [
"Final Fantasy VII"
]
}

View File

@@ -1,39 +0,0 @@
{
"character_id": "yuna_(ff10)",
"character_name": "Yuna",
"identity": {
"base_specs": "1girl, slender, fair skin",
"hair": "short brown hair, bob cut",
"eyes": "heterochromia, blue eye, green eye",
"expression": "gentle",
"hands": "",
"arms": "",
"torso": "small breasts",
"pelvis": "",
"legs": "",
"feet": "",
"distinguishing_marks": ""
},
"wardrobe": {
"inner_layer": "white kimono top, yellow obi",
"outer_layer": "",
"lower_body": "long blue skirt, floral pattern",
"footwear": "boots",
"gloves": "detached sleeves",
"accessories": "summoner staff, necklace"
},
"styles": {
"aesthetic": "fantasy, final fantasy x style",
"primary_color": "white",
"secondary_color": "blue",
"tertiary_color": "yellow"
},
"lora": {
"lora_name": "",
"lora_weight": 0.8,
"lora_triggers": ""
},
"tags": [
"Final Fantasy X"
]
}

View File

@@ -16,7 +16,7 @@
}, },
"4": { "4": {
"inputs": { "inputs": {
"ckpt_name": "Noob/oneObsession_v19Atypical.safetensors" "ckpt_name": ""
}, },
"class_type": "CheckpointLoaderSimple" "class_type": "CheckpointLoaderSimple"
}, },
@@ -169,5 +169,45 @@
"clip": ["4", 1] "clip": ["4", 1]
}, },
"class_type": "LoraLoader" "class_type": "LoraLoader"
},
"17": {
"inputs": {
"lora_name": "",
"strength_model": 0.8,
"strength_clip": 0.8,
"model": ["16", 0],
"clip": ["16", 1]
},
"class_type": "LoraLoader"
},
"18": {
"inputs": {
"lora_name": "",
"strength_model": 1.0,
"strength_clip": 1.0,
"model": ["17", 0],
"clip": ["17", 1]
},
"class_type": "LoraLoader"
},
"19": {
"inputs": {
"lora_name": "",
"strength_model": 1.0,
"strength_clip": 1.0,
"model": ["18", 0],
"clip": ["18", 1]
},
"class_type": "LoraLoader"
},
"20": {
"inputs": {
"lora_name": "",
"strength_model": 1.0,
"strength_clip": 1.0,
"model": ["19", 0],
"clip": ["19", 1]
},
"class_type": "LoraLoader"
} }
} }

View File

@@ -0,0 +1,24 @@
{
"action_id": "3p_sex_000037",
"action_name": "3P Sex 000037",
"action": {
"base": "threesome, group_sex, 3_people",
"head": "blush, half-closed_eyes, sweat",
"upper_body": "nude, reaching",
"lower_body": "sex, spread_legs, intercourse",
"hands": "groping",
"feet": "toes_curled",
"additional": "panting, orgasm, sweat"
},
"lora": {
"lora_name": "Illustrious/Poses/3P_SEX-000037.safetensors",
"lora_weight": 0.8,
"lora_triggers": "threesome, group_sex",
"lora_weight_min": 0.8,
"lora_weight_max": 0.8
},
"tags": {
"participants": "threesome",
"nsfw": true
}
}

24
data/actions/4p_sex.json Normal file
View File

@@ -0,0 +1,24 @@
{
"action_id": "4p_sex",
"action_name": "4P Sex",
"action": {
"base": "group_sex, foursome, 4p, multiple_partners, sexual_act",
"head": "moaning, open_mouth, closed_eyes, ahegao, heart_eyes, heavy_breathing, flushed_face, sweat",
"upper_body": "nude, arching_back, standing_on_all_fours, breasts_press, breast_grab, fingers_in_mouth",
"lower_body": "vaginal_intercourse, anal_intercourse, double_penetration, legs_apart, kneeing, missionary_position",
"hands": "breast_fondling, clutching_sheets, on_partner, touching_self",
"feet": "toes_curled, barefoot",
"additional": "bodily_fluids, semen, sweat, messy, erotic, intimate"
},
"lora": {
"lora_name": "Illustrious/Poses/4P_sex.safetensors",
"lora_weight": 0.6,
"lora_triggers": "4P_sexV1",
"lora_weight_min": 0.6,
"lora_weight_max": 0.6
},
"tags": {
"participants": "4people",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "_malebolgia__oral_sex_tounge_afterimage_concept_2_0_illustrious",
"action_name": "Malebolgia Oral Sex Tounge Afterimage Concept 2 0 Illustrious",
"action": {
"base": "kneeling, leaning_forward, oral_sex",
"head": "looking_up, mouth_open, intense_expression, half-closed_eyes",
"upper_body": "forward_bend, reaching_forward",
"lower_body": "kneeling, legs_spread",
"hands": "grabbing_partner, hand_on_thigh",
"feet": "barefoot",
"additional": "afterimage, motion_blur, multiple_tongues, speed_lines, saliva_drool"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/[Malebolgia] Oral Sex Tounge Afterimage Concept 2.0 Illustrious.safetensors",
"lora_weight": 1.0,
"lora_triggers": "[Malebolgia] Oral Sex Tounge Afterimage Concept 2.0 Illustrious",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "actually_reliable_penis_kissing_3_variants_illustrious",
"action_name": "Actually Reliable Penis Kissing 3 Variants Illustrious",
"action": {
"base": "kneeling, fellatio, oral sex, kissing penis",
"head": "eyes closed, half-closed eyes, looking up, kissing, tongue",
"upper_body": "arched back, leaning forward",
"lower_body": "kneeling",
"hands": "holding penis, hand on thigh",
"feet": "",
"additional": "saliva, slime, connection"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/Actually_Reliable_Penis_Kissing_3_Variants_Illustrious.safetensors",
"lora_weight": 1.0,
"lora_triggers": "Actually_Reliable_Penis_Kissing_3_Variants_Illustrious",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "after_sex_fellatio_illustriousxl_lora_nochekaiser_r1",
"action_name": "After Sex Fellatio Illustriousxl Lora Nochekaiser R1",
"action": {
"base": "1girl, 1boy, completely_nude, lying, on_back, spread_legs",
"head": "looking_at_viewer, tongue, open_mouth, blush, disheveled_hair, half-closed_eyes",
"upper_body": "on_bed, large_breasts, nipples, sweat",
"lower_body": "pussy, cum_in_pussy, leaking_cum, knees_up, thighs_apart",
"hands": "on_bed",
"feet": "barefoot",
"additional": "after_sex, fellatio, penis, cum, cum_drip, messy_body, sweat, bed_sheet"
},
"lora": {
"lora_name": "Illustrious/Poses/after-sex-fellatio-illustriousxl-lora-nochekaiser_r1.safetensors",
"lora_weight": 1.0,
"lora_triggers": "after sex fellatio",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "afterfellatio_ill",
"action_name": "Afterfellatio Ill",
"action": {
"base": "kneeling, leaning_forward, pov",
"head": "looking_at_viewer, blush, tilted_head, cum_on_face, half-closed_eyes, tears, messy_makeup",
"upper_body": "arms_down, reaching_towards_viewer",
"lower_body": "kneeling",
"hands": "hands_on_penis",
"feet": "barefoot",
"additional": "cum, cum_string, cum_in_mouth, closed_mouth, stringy_cum"
},
"lora": {
"lora_name": "Illustrious/Poses/afterfellatio_ill.safetensors",
"lora_weight": 0.8,
"lora_triggers": "after fellatio, cum in mouth, closed mouth, cum string",
"lora_weight_min": 0.8,
"lora_weight_max": 0.8
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "afteroral",
"action_name": "Afteroral",
"action": {
"base": "after_fellatio, post-coital, flushed_face, messy_appearance",
"head": "messy_hair, panting, heavy_breathing, half-closed_eyes, dazed, pupil_dilated, flushed, sweat",
"upper_body": "smeared_lipstick, runny_makeup, saliva, saliva_trail, cum_on_face, cum_on_mouth, excessive_cum",
"lower_body": "",
"hands": "hands_near_face, curled_fingers",
"feet": "",
"additional": "after_fellatio, cum_drip, mess"
},
"lora": {
"lora_name": "Illustrious/Poses/afteroral.safetensors",
"lora_weight": 1.0,
"lora_triggers": "after oral, after deepthroat",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "afterpaizuri",
"action_name": "Afterpaizuri",
"action": {
"base": "kneeling, sitting, exhausted, post-coital, afterpaizuri",
"head": "flushed, messy_hair, panting, mouth_open, tongue_out, half-closed_eyes, dazed, looking_at_viewer",
"upper_body": "bare_shoulders, cleavage, semen_on_breasts, disheveled_clothes, bra_removed_under_clothes",
"lower_body": "kneeling, thighs_together, buttocks_on_heels",
"hands": "hands_on_lap",
"feet": "barefoot",
"additional": "semen_on_face, heavy_breathing, sweat, moisture, viscous_liquid, creamy_fluid"
},
"participants": {
"solo_focus": "true",
"orientation": "F"
},
"lora": {
"lora_name": "Illustrious/Poses/afterpaizuri.safetensors",
"lora_weight": 1.0,
"lora_triggers": "afterpaizuri",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "aftersexbreakv2",
"action_name": "Aftersexbreakv2",
"action": {
"base": "lying, on_back, on_bed, bowlegged_pose, spread_legs, twisted_torso",
"head": "messy_hair, head_back, sweaty, rolling_eyes, half-closed_eyes, ahegao",
"upper_body": "arms_spread, arms_above_head, sweat, naked, collarbone, heavy_breathing",
"lower_body": "hips, navel, female_pubic_hair, spread_legs, legs_up, bent_legs",
"hands": "relaxed_hands",
"feet": "barefoot",
"additional": "condom_wrapper, used_tissue, stained_sheets, cum_on_bed, sweat_bead"
},
"lora": {
"lora_name": "Illustrious/Poses/AfterSexBreakV2.safetensors",
"lora_weight": 1.0,
"lora_triggers": "aftersexbreak, after sex, male sitting",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "against_glass_bs",
"action_name": "Against Glass Bs",
"action": {
"base": "against_glass, leaning_forward, pressed_against_surface, glass_surface",
"head": "face_pressed, cheek_press, breath_on_glass, looking_at_viewer, intimate_gaze",
"upper_body": "chest_pressed_against_glass, leaning_into_glass",
"lower_body": "hips_pushed_forward, standing",
"hands": "hands_on_glass, palms_on_glass, fingers_spread",
"feet": "standing",
"additional": "condensation, translucent_surface, distortion"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/Against_glass_bs.safetensors",
"lora_weight": 1.0,
"lora_triggers": "Against_glass_bs",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "amateur_pov_filming",
"action_name": "Amateur Pov Filming",
"action": {
"base": "pov, selfie, solo, indoors, recording, smartphone, holding_phone",
"head": "looking_at_viewer, blush, open_mouth",
"upper_body": "upper_body, breasts, nipples",
"lower_body": "hips",
"hands": "holding_phone, holding_id_card",
"feet": "barefoot",
"additional": "mirror_selfie, amateur_aesthetic"
},
"lora": {
"lora_name": "Illustrious/Poses/Amateur_POV_Filming.safetensors",
"lora_weight": 1.0,
"lora_triggers": "Homemade_PornV1, recording, pov, prostitution",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "arch_back_sex_v1_1_illustriousxl",
"action_name": "Arch Back Sex V1 1 Illustriousxl",
"action": {
"base": "doggystyle, sex_from_behind, all_fours",
"head": "head_back, looking_back, closed_eyes, blush",
"upper_body": "arms_support, arched_back",
"lower_body": "lifted_hip, kneeling, spread_legs",
"hands": "grabbing_butt",
"feet": "toes",
"additional": "kiss, sweat, saliva, intense_pleasure"
},
"lora": {
"lora_name": "Illustrious/Poses/arch-back-sex-v1.1-illustriousxl.safetensors",
"lora_weight": 1.0,
"lora_triggers": "kiss, arched_back, sex_from_behind",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "arm_grab_missionary_ill_10",
"action_name": "Arm Grab Missionary Ill 10",
"action": {
"base": "missionary, lying, on_back, sex, vaginal_sex",
"head": "blushing, open_mouth, one_eye_closed, looking_at_viewer, dilated_pupils",
"upper_body": "arms_above_head, pinned, restrained, grabbed_wrists, breasts, nipples, medium_breasts",
"lower_body": "legs_spread, lifted_pelvis, spread_legs, legs_up, knees_up",
"hands": "interlocked_fingers, holding_hands",
"feet": "barefoot",
"additional": "faceless_male, sweat, motion_lines"
},
"lora": {
"lora_name": "Illustrious/Poses/arm_grab_missionary_ill-10.safetensors",
"lora_weight": 1.0,
"lora_triggers": "arm_grab_missionary",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "ballsdeep_il_v2_2_s",
"action_name": "Ballsdeep Il V2 2 S",
"action": {
"base": "sexual_intercourse, deep_penetration, thrusting, mating_press, from_behind, doggy_style, missionary, cowgirl_position",
"head": "eyes_rolled_back, closed_eyes, open_mouth, flushed_face, sweat, expressionless, intense_expression",
"upper_body": "arched_back, hands_on_own_body, grabbing, fingers_clenched, sweating",
"lower_body": "joined_genitals, spread_legs, wrap_legs, hips_thrusting, stomach_bulge, testicles_pressed",
"hands": "finger_clutch, gripping",
"feet": "curled_toes",
"additional": "intense, deep_pen, skin_on_skin"
},
"lora": {
"lora_name": "Illustrious/Poses/BallsDeep-IL-V2.2-S.safetensors",
"lora_weight": 1.0,
"lora_triggers": "deep penetration",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "bathingtogether",
"action_name": "Bathingtogether",
"action": {
"base": "bathing, sitting, partially_submerged, bathtub",
"head": "looking_at_viewer, eye_contact",
"upper_body": "bare_shoulders, skin_to_skin, arms_around_each_other",
"lower_body": "underwater, knees_up",
"hands": "hands_on_body",
"feet": "barefoot",
"additional": "bubbles, water, steam, wet, wet_hair, mirror_reflection"
},
"lora": {
"lora_name": "Illustrious/Poses/bathingtogether.safetensors",
"lora_weight": 1.0,
"lora_triggers": "bathing together",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "2girls",
"nsfw": false
}
}

View File

@@ -0,0 +1,24 @@
{
"action": {
"base": "2koma, side-by-side, comparison",
"head": "facial, cum_on_face, messy_face, eyes_closed, mouth_open, orgasm",
"upper_body": "semi-nude, messy, cum_on_breasts",
"lower_body": "",
"hands": "hands_on_head",
"feet": "",
"additional": "cum, close-up, ejaculation"
},
"action_id": "before_after_1230829",
"action_name": "Before After 1230829",
"lora": {
"lora_name": "Illustrious/Poses/before_after_1230829.safetensors",
"lora_triggers": "before_after",
"lora_weight": 0.9,
"lora_weight_max": 0.7,
"lora_weight_min": 0.6
},
"tags": {
"participants": "1girl",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "belly_dancing",
"action_name": "Belly Dancing",
"action": {
"base": "belly_dancing, dancing, standing",
"head": "",
"upper_body": "arms_up",
"lower_body": "swaying",
"hands": "own_hands_together",
"feet": "",
"additional": ""
},
"participants": {
"solo_focus": "false",
"orientation": "F"
},
"lora": {
"lora_name": "",
"lora_weight": 1.0,
"lora_triggers": "",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "bentback",
"action_name": "Bentback",
"action": {
"base": "bent_over, from_behind, arched_back",
"head": "looking_back, looking_at_viewer",
"upper_body": "twisted_torso, arms_at_sides",
"lower_body": "ass_focus, kneepits, spread_legs",
"hands": "hands_on_legs",
"feet": "barefoot",
"additional": "leaning_forward"
},
"lora": {
"lora_name": "Illustrious/Poses/BentBack.safetensors",
"lora_weight": 1.0,
"lora_triggers": "bentback, kneepits",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "blowjobcomicpart2",
"action_name": "Blowjobcomicpart2",
"action": {
"base": "3koma, comic_strip, sequence, vertical_strip",
"head": "tongue_out, open_mouth, saliva, empty_eyes, rolled_eyes",
"upper_body": "arms_at_sides, torso, standing_on_knees",
"lower_body": "sexual_activity, kneeling",
"hands": "hands_on_penis, grasping",
"feet": "out_of_frame",
"additional": "fellatio, irrumatio, licking, ejaculation, cum_in_mouth, excessive_cum"
},
"lora": {
"lora_name": "Illustrious/Poses/BlowjobComicPart2.safetensors",
"lora_weight": 1.0,
"lora_triggers": "bjmcut",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "bodybengirl",
"action_name": "Bodybengirl",
"action": {
"base": "suspended_congress, lifted_by_torso, torso_grab",
"head": "",
"upper_body": "arms_up, bent_over",
"lower_body": "legs_up",
"hands": "",
"feet": "",
"additional": "1boy, 1girl, size_difference, loli"
},
"lora": {
"lora_name": "Illustrious/Poses/BodyBenGirl.safetensors",
"lora_weight": 1.0,
"lora_triggers": "bentstand-behind",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1boy 1girl",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "bodybengirlpart2",
"action_name": "Bodybengirlpart2",
"action": {
"base": "suspended, from_side, bent_over, torso_grab",
"head": "embarrassed, sweating, scared, mouth_open, looking_away, downcast",
"upper_body": "arms_at_sides, limp_arms",
"lower_body": "legs_dangling, knees_together, bare_feet, feet_dangling",
"hands": "open_hands, limp_hands",
"feet": "bare_feet, dangling",
"additional": "motion_lines, sweat_drops"
},
"lora": {
"lora_name": "Illustrious/Poses/BodyBenGirlPart2.safetensors",
"lora_weight": 0.9,
"lora_triggers": "bentstand-behind, dangling legs, dangling arms, from_side, arms hanging down, torso_grab, suspended",
"lora_weight_min": 0.9,
"lora_weight_max": 0.9
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "bored_retrain_000115_1336316",
"action_name": "Bored Retrain 000115 1336316",
"action": {
"base": "bored, slouching, leaning_forward, ennui, uninterested",
"head": "head_on_hand, blank_stare, half-lidded_eyes",
"upper_body": "slumped, leaning_forward, sitting, arms_crossed",
"lower_body": "sitting, legs_crossed",
"hands": "hand_on_cheek, hand_on_chin",
"feet": "",
"additional": "sighing, waiting"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/Bored_Retrain-000115_1336316.safetensors",
"lora_weight": 1.0,
"lora_triggers": "Bored_Retrain-000115_1336316",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "breast_pressh",
"action_name": "Breast Pressh",
"action": {
"base": "1boy, 2girls, sandwiched, standing, height_difference, size_difference",
"head": "head_between_breasts, face_between_breasts, cheek_squash",
"upper_body": "breast_press, hugging, arms_around_waist, chest_to_chest",
"lower_body": "hips_touching, legs_apart",
"hands": "hands_on_back",
"feet": "barefoot",
"additional": "hetero, multiple_girls"
},
"lora": {
"lora_name": "Illustrious/Poses/breast_pressH.safetensors",
"lora_weight": 0.6,
"lora_triggers": "breast_pressH",
"lora_weight_min": 0.6,
"lora_weight_max": 0.6
},
"tags": {
"participants": "1boy 2girls",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "breast_smother_illustriousxl_lora_nochekaiser",
"action_name": "Breast Smother Illustriousxl Lora Nochekaiser",
"action": {
"base": "breast_smother, face_in_cleavage, intimate, (POV:1.2), side_view",
"head": "looking_down, half-closed_eyes, affectionate, dominant",
"upper_body": "large_breasts, cleavage, squished_breasts, hugging, embracing, chest_press",
"lower_body": "close_contact, standing",
"hands": "cradling_head, fingers_in_hair, pressing_head_to_breasts",
"feet": "barefoot, feet_on_ground",
"additional": "skin_compression, soft_lighting, motorboating"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/breast-smother-illustriousxl-lora-nochekaiser.safetensors",
"lora_weight": 1.0,
"lora_triggers": "breast-smother-illustriousxl-lora-nochekaiser",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "breast_sucking_fingering_illustriousxl_lora_nochekaiser",
"action_name": "Breast Sucking Fingering Illustriousxl Lora Nochekaiser",
"action": {
"base": "duo, sex, sexual_intercourse, close-up, breast_sucking, fingering, intimate_embrace",
"head": "face_buried_in_breasts, sucking_nipple, nipples_suck, saliva, eyes_closed, heavy_breathing, blush, expression_of_bliss",
"upper_body": "reaching_down, holding_partner, arm_around_waist, large_breasts, exposed_breasts, nude_torso, pressing_bodies",
"lower_body": "legs_spread, pussy_exposed, vaginal_fingering, m_legs, intertwined_legs",
"hands": "fingering, fingers_inside, clitoris_rubbing, squeezing_breasts, groping",
"feet": "toes_curled, relaxed_feet",
"additional": "saliva_trail, sweat, sweat_drop, uncensored"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/breast-sucking-fingering-illustriousxl-lora-nochekaiser.safetensors",
"lora_weight": 1.0,
"lora_triggers": "breast-sucking-fingering-illustriousxl-lora-nochekaiser",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "brokenglass_illusxl_incrs_v1",
"action_name": "Brokenglass Illusxl Incrs V1",
"action": {
"base": "breaking_through_barrier, dynamic_pose, impact_frame, dynamic_angle",
"head": "intense_expression, facial_focus, visible_through_glass",
"upper_body": "reaching_towards_viewer, twisted_torso, arms_extended",
"lower_body": "dynamic_pose, movement",
"hands": "hands_up, touching_glass, debris_interaction",
"feet": "grounded, dynamic",
"additional": "broken_glass, glass_shards, shattered, spiderweb_cracks, cinematic_lighting, refraction, debris, explosive_impact"
},
"participants": {
"solo_focus": "true",
"orientation": "F"
},
"lora": {
"lora_name": "Illustrious/Poses/BrokenGlass_illusXL_Incrs_v1.safetensors",
"lora_weight": 1.0,
"lora_triggers": "BrokenGlass_illusXL_Incrs_v1",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "butt_smother_ag_000043",
"action_name": "Butt Smother Ag 000043",
"action": {
"base": "1girl, 1boy, facesitting, pov, first-person_view, dominant_woman, submissive_man",
"head": "looking_down, looking_away, half-closed_eyes, seductive_expression, facial_expression",
"upper_body": "arms_behind_back, arched_back, upper_body_lean",
"lower_body": "buttocks, ass_focus, heavy_buttocks, spread_legs, kneeling, thighs_straddling",
"hands": "hands_on_thighs, hands_on_head",
"feet": "feet_together, curled_toes",
"additional": "extreme_close-up, squashed, muffling, soft_lighting, skin_texture"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/Butt_smother_ag-000043.safetensors",
"lora_weight": 1.0,
"lora_triggers": "Butt_smother_ag-000043",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

28
data/actions/buttjob.json Normal file
View File

@@ -0,0 +1,28 @@
{
"action_id": "buttjob",
"action_name": "Buttjob",
"action": {
"base": "buttjob, intercrural_sex, sex_positions",
"head": "",
"upper_body": "bent_over",
"lower_body": "buttjob, genitals_between_buttocks",
"hands": "hands_on_own_thighs",
"feet": "",
"additional": "missionary_position"
},
"participants": {
"solo_focus": "true",
"orientation": "MF"
},
"lora": {
"lora_name": "Illustrious/Poses/buttjob.safetensors",
"lora_weight": 1.0,
"lora_triggers": "buttjob",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "carwashv2",
"action_name": "Carwashv2",
"action": {
"base": "washing_vehicle, bending_over, suggestive_pose",
"head": "wet_hair",
"upper_body": "wet_clothes, breast_press, cleavage, breasts_on_glass",
"lower_body": "",
"hands": "holding_sponge, holding_hose",
"feet": "",
"additional": "car, motor_vehicle, soap_bubbles, wet, water_splashing"
},
"lora": {
"lora_name": "Illustrious/Poses/CarWashV2.safetensors",
"lora_weight": 0.8,
"lora_triggers": "w4sh1n, w4sh0ut",
"lora_weight_min": 0.8,
"lora_weight_max": 0.8
},
"tags": {
"participants": "1girl",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "cat_stretchill",
"action_name": "Cat Stretchill",
"action": {
"base": "cat_stretch, pose, all_fours",
"head": "head_down, looking_at_viewer, closed_eyes",
"upper_body": "arched_back, outstretched_arms, chest_support, hands_on_ground",
"lower_body": "hips_up, buttocks_up, kneeling, knees_on_ground",
"hands": "palms_down",
"feet": "feet_up",
"additional": "trembling, cat_tail, cat_ears"
},
"lora": {
"lora_name": "Illustrious/Poses/cat_stretchILL.safetensors",
"lora_weight": 0.7,
"lora_triggers": "stretching, cat stretch, downward dog, trembling",
"lora_weight_min": 0.7,
"lora_weight_max": 0.7
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "charm_person_magic",
"action_name": "Charm Person Magic",
"action": {
"base": "casting_spell, magic_circle, spell_casting",
"head": "smile, confident_expression, glowing_eyes, looking_at_viewer",
"upper_body": "outstretched_hand, reaching_towards_viewer, arms_up, upper_body",
"lower_body": "",
"hands": "open_hand, hand_gesture",
"feet": "",
"additional": "aura, light_particles, glittering, magical_energy"
},
"lora": {
"lora_name": "Illustrious/Poses/charm_person_magic.safetensors",
"lora_weight": 0.7,
"lora_triggers": "charm_person_(magic), cham_aura",
"lora_weight_min": 0.7,
"lora_weight_max": 0.7
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "cheekbulge",
"action_name": "Cheekbulge",
"action": {
"base": "fellatio",
"head": "cheek_bulge, head_tilt, saliva, penis_in_mouth, fellatio, looking_up, mouth_fill",
"upper_body": "arms_behind_back, upper_body",
"lower_body": "kneeling, kneeling_position",
"hands": "hands_on_head",
"feet": "plantar_flexion",
"additional": "deepthroat, pov, penis, male_focus"
},
"lora": {
"lora_name": "Illustrious/Poses/cheekbulge.safetensors",
"lora_weight": 1.0,
"lora_triggers": "cheek bulge, male pov",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "chokehold",
"action_name": "Chokehold",
"action": {
"base": "rear_naked_choke, from_behind, struggling, kneeling",
"head": "head_back, open_mouth, rolling_eyes, teardrops, veins, suffocation, air_hunger",
"upper_body": "arm_around_neck, struggling, grabbing_arm, posture_leaned_forward, arched_back",
"lower_body": "kneeling, bent_over, spread_legs",
"hands": "clenched_hands, struggling",
"feet": "barefoot, toes_curled",
"additional": "distress, blushing, drooling, saliva, sweat"
},
"lora": {
"lora_name": "Illustrious/Poses/chokehold.safetensors",
"lora_weight": 1.0,
"lora_triggers": "choke hold",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "cleavageteasedwnsty_000008",
"action_name": "Cleavageteasedwnsty 000008",
"action": {
"base": "leaning_forward, sexually_suggestive",
"head": "looking_at_viewer, smile, blush, one_eye_closed, blue_eyes",
"upper_body": "arms_bent_at_elbows, cleavage, breasts_squeezed_together, areola_slip, bare_shoulders, collarbone",
"lower_body": "",
"hands": "hands_on_own_chest, pulling_down_clothes, adjusting_clothes",
"feet": "",
"additional": "teasing, undressing"
},
"lora": {
"lora_name": "Illustrious/Poses/CleavageTeaseDwnsty-000008.safetensors",
"lora_weight": 1.0,
"lora_triggers": "pulling down own clothes, teasing",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action": {
"base": "close-up, portrait, face_focus",
"head": "open_mouth, tongue, saliva, blush, looking_at_viewer, eyes_visible",
"upper_body": "",
"lower_body": "",
"hands": "",
"feet": "",
"additional": "cum, messy, fluids"
},
"action_id": "closeup_facial_illus",
"action_name": "Closeup Facial Illus",
"lora": {
"lora_name": "Illustrious/Poses/Closeup_Facial_iLLus.safetensors",
"lora_triggers": "Closeup Facial",
"lora_weight": 1,
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": true
}
}

28
data/actions/cof.json Normal file
View File

@@ -0,0 +1,28 @@
{
"action_id": "cof",
"action_name": "Cum on Figure",
"action": {
"base": "faux_figurine, miniature, cum_on_body",
"head": "cum_on_face",
"upper_body": "",
"lower_body": "",
"hands": "",
"feet": "",
"additional": "cum, excessive_cum"
},
"participants": {
"solo_focus": "true",
"orientation": "F"
},
"lora": {
"lora_name": "Illustrious/Poses/cof.safetensors",
"lora_weight": 1.0,
"lora_triggers": "cof",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": true
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "cooperative_grinding",
"action_name": "Cooperative Grinding",
"action": {
"base": "duo, standing, lift_and_carry, straddling, legs_wrapped_around_waist, physical_contact",
"head": "head_thrown_back, blushing, panting, eyes_closed, half-closed_eyes, rolled_back_eyes, expressionless_pleasure",
"upper_body": "arms_around_neck, holding_buttocks, supporting_thighs, chest_to_chest, close_embrace",
"lower_body": "hips_touching, grinding, mating_press, pelvic_curtain, thighs_spread, lifted_legs",
"hands": "grabbing, gripping_back, squeezing",
"feet": "arched_toes, dangling_feet",
"additional": "sweat, motion_lines, intimate, sexual_intercourse, erotic"
},
"participants": {
"solo_focus": "false",
"orientation": "MFF"
},
"lora": {
"lora_name": "Illustrious/Poses/cooperative_grinding.safetensors",
"lora_weight": 1.0,
"lora_triggers": "cooperative_grinding",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1boy 2girls",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "cooperativepaizuri",
"action_name": "Cooperativepaizuri",
"action": {
"base": "cooperative_paizuri, 2girls, 1boy, sexual_activity, sex_position",
"head": "smile, open_mouth, facial, looking_at_partner, eyes_closed, flushed",
"upper_body": "arms_around_neck, breasts_between_breasts, large_breasts, cleavage, nipple_tweaking",
"lower_body": "penis, glans, erection, kneeling, straddling, cowgirl_position",
"hands": "holding_penis, touching_partner, reaching_for_partner",
"feet": "barefoot, toes_curled",
"additional": "pov, cum_on_body, fluids, (multiple_girls:1.2), erotic, masterpiece"
},
"lora": {
"lora_name": "Illustrious/Poses/cooperativepaizuri.safetensors",
"lora_weight": 1.0,
"lora_triggers": "cooperative paizuri",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "2girls 1boy",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "covering_privates_illustrious_v1_0",
"action_name": "Covering Privates Illustrious V1 0",
"action": {
"base": "covering_genitals, covering_breasts",
"head": "blush, embarrassed, looking_at_viewer",
"upper_body": "arms_crossed, covering_breasts, upper_body",
"lower_body": "legs_together, hips",
"hands": "covering_crotch",
"feet": "standing",
"additional": "modesty"
},
"lora": {
"lora_name": "Illustrious/Poses/covering privates_illustrious_V1.0.safetensors",
"lora_weight": 1.0,
"lora_triggers": "covering privates, covering crotch, covering breasts",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": true
}
}

View File

@@ -0,0 +1,24 @@
{
"action_id": "coveringownmouth_ill_v1",
"action_name": "Coveringownmouth Ill V1",
"action": {
"base": "covering_own_mouth, solo",
"head": "hand_over_mouth",
"upper_body": "arm_raised",
"lower_body": "variable",
"hands": "palm_inward",
"feet": "variable",
"additional": "pensive, shy, embarrassed, surprise"
},
"lora": {
"lora_name": "Illustrious/Poses/CoveringOwnMouth_Ill_V1.safetensors",
"lora_weight": 1.0,
"lora_triggers": "covering_own_mouth",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "solo",
"nsfw": false
}
}

View File

@@ -0,0 +1,28 @@
{
"action_id": "cowgirl_position_breast_press_illustriousxl_lora_nochekaiser",
"action_name": "Cowgirl Position Breast Press Illustriousxl Lora Nochekaiser",
"action": {
"base": "straddling, pov, on_top, leaning_forward",
"head": "looking_at_viewer, intense_gaze, half-closed_eyes",
"upper_body": "arms_extended, breasts_pressed_against_viewer, breast_squish, cleavage",
"lower_body": "spread_legs, knees_up, straddling_partner",
"hands": "on_man's_chest, hand_on_partner",
"feet": "out_of_frame",
"additional": "breast_deformation, intimate, close-up, first-person_view"
},
"participants": {
"solo_focus": "false",
"orientation": "MFF"
},
"lora": {
"lora_name": "Illustrious/Poses/cowgirl-position-breast-press-illustriousxl-lora-nochekaiser.safetensors",
"lora_weight": 1.0,
"lora_triggers": "cowgirl-position-breast-press-illustriousxl-lora-nochekaiser",
"lora_weight_min": 1.0,
"lora_weight_max": 1.0
},
"tags": {
"participants": "1girl 1boy",
"nsfw": true
}
}

Some files were not shown because too many files have changed in this diff Show More