comfyanonymous/ComfyUI
Asset system
A content-addressed inventory of every model, input, and output ComfyUI knows about. Implemented in app/assets/ and backed by SQLite. Disabled by default; opted in with --enable-assets.
Purpose
Track the files in models/, input/, and output/ so the frontend can:
- Search and filter by tag, mime type, mtime, or hash.
- Show previews/thumbnails for outputs.
- Detect duplicates (same content under multiple paths).
- Surface system metadata (resolution, duration, sample rate, …) without re-reading every file.
Layout
app/assets/
├── helpers.py # get_utc_now and friends
├── scanner.py # 20 KB — walk directories, diff against DB, enqueue work
├── seeder.py # 29 KB — lifecycle (start, pause, enqueue_enrich, shutdown)
├── api/
│ ├── routes.py # 28 KB — /assets/* HTTP routes
│ ├── schemas_in.py # 11 KB — request schemas
│ ├── schemas_out.py # 2 KB — response schemas
│ └── upload.py # 6 KB — upload endpoints
├── database/
│ ├── models.py # Asset, AssetReference, AssetReferenceMeta, AssetReferenceTag, Tag
│ └── queries/ # Read-side queries
└── services/
├── ingest.py # 17 KB — register a single file
├── bulk_ingest.py # 8 KB — batch import
├── asset_management.py # 11 KB — resolve hash → path, etc.
├── metadata_extract.py # 11 KB — image/video/audio metadata
├── hashing.py # BLAKE3 and SHA helpers
├── path_utils.py # Safe path normalization
├── tagging.py # Tag normalization
├── file_utils.py # I/O helpers
└── schemas.py # Internal data classesData model
erDiagram
ASSETS ||--o{ ASSET_REFERENCES : "1..n"
ASSET_REFERENCES ||--o{ ASSET_REFERENCE_META : "0..n"
ASSET_REFERENCES ||--o{ ASSET_REFERENCE_TAGS : "0..n"
TAGS ||--o{ ASSET_REFERENCE_TAGS : "0..n"
ASSET_REFERENCES ||--o| ASSET_REFERENCES : "preview_id"| Table | Key columns | Purpose |
|---|---|---|
assets |
id (uuid), hash, size_bytes, mime_type |
One row per content-distinct file (deduplicated by hash) |
asset_references |
id, asset_id, file_path, name, owner_id, mtime_ns, enrichment_level, system_metadata, … |
One row per (path or API-only) reference to an asset |
asset_reference_meta |
(asset_reference_id, key, ordinal) PK + value columns |
Flexible KV metadata on a reference |
asset_reference_tags |
(asset_reference_id, tag_name) PK + origin |
Many-to-many tag join |
tags |
name PK + tag_type |
Tag dictionary |
The full schema is in app/assets/database/models.py. Naming conventions are pinned in app/database/models.py so Alembic-generated migrations have stable index/constraint names.
Enrichment levels
Each AssetReference carries an enrichment_level (0/1/2):
- 0 — basic record only (path + size + mtime).
- 1 — MIME type and structural metadata read.
- 2 — full content hash + thumbnail/preview computed.
Background work upgrades references over time so initial scans are fast and the expensive bits happen lazily.
Scanner / seeder
graph LR
Start[main.py setup_database] -->|--enable-assets| Seeder[asset_seeder.start]
Seeder --> Scanner[scanner walk]
Scanner -->|new file| Ingest[services.ingest.register_file_in_place]
Ingest --> DB[(SQLite)]
Scanner -->|stale ref| Mark[mark is_missing=true]
Seeder --> Enrich[enqueue_enrich]
Enrich --> Hash[hashing + metadata_extract]
Hash --> DB
PromptWorker[prompt_worker] -->|asset_seeder.pause / resume| Seeder
PromptWorker -->|enqueue_enrich roots=output| SeederThe seeder runs in a background thread (or thread pool, depending on configuration). It pauses while the prompt worker is executing, then resumes between prompts. Output files registered after a prompt completes get an immediate enrichment pass so they become searchable.
asset_seeder.start(roots=("models", "input", "output"), prune_first=True, compute_hashes=True) is the canonical startup call from main.py. Pass is_disabled=True in tests to skip background work.
Ingest
services/ingest.py:register_file_in_place(...) is the single-file entry: given a path, it resolves whether the file already corresponds to an existing Asset (by hash), creates an AssetReference if not, and updates system_metadata from metadata_extract.
bulk_ingest.py batches this for one-shot imports — useful when a user drops a folder of inputs into the UI.
Metadata extraction
services/metadata_extract.py reads:
- Images — width, height, format, color space (uses
PIL). - Videos — duration, frame rate, dimensions (uses
av). - Audio — sample rate, channels, duration.
- Safetensors — header metadata for model files (architecture hints, license tags, …).
Extracted values land in system_metadata (a JSON column on AssetReference).
REST API
The HTTP routes in app/assets/api/routes.py live behind /assets/.... Notable ones:
GET /assets— list with filters (mime, tag, hash, owner)GET /assets/{id}POST /assets/upload— multipart uploadPATCH /assets/{id}— update tags or user metadataDELETE /assets/{id}— soft-delete
Schemas are Pydantic in schemas_in.py (request) and schemas_out.py (response). Routes are mounted by register_assets_routes from server.py.
File locking and multi-process
app/database/db.py acquires an OS-level lock on <db>.lock via filelock. If two ComfyUI processes try to share the same DB they get a clear "database is locked" error. Use --database-url to point each process at its own file.
Integration points
- Bootstrapped in
setup_databaseinmain.py. - Routes mounted in
server.py. _collect_output_absolute_pathsinmain.pyfeeds completed prompt outputs intoregister_output_files(asset-services side) so they become assets immediately.- Migrations in
alembic_db/versions/. - Tested in
tests-unit/assets_test/andtests-unit/seeder_test/, plustests/test_asset_seeder.py.
Where to start a change
- Adding a new metadata field: extend
AssetReferenceMeta(key/value pattern, no schema change) for free-form fields, or add a column toAssetReferenceand write an Alembic migration for indexed fields. - New REST endpoint: add it to
app/assets/api/routes.py; add request/response schemas. - New metadata extractor: extend
services/metadata_extract.py; add a unit test undertests-unit/assets_test/. - New tag origin: extend
AssetReferenceTag.originvalues and updatetagging.py.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.