mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-06 19:42:34 +08:00
Architecture changes: - API Routes -> manager.py (thin adapter) -> services/ (business logic) -> queries/ (atomic DB ops) - Services own session lifecycle via create_session() - Queries accept Session as parameter, do single-table atomic operations New app/assets/services/ layer: - __init__.py - exports all service functions - ingest.py - ingest_file_from_path(), register_existing_asset() - asset_management.py - get_asset_detail(), update_asset_metadata(), delete_asset_reference(), set_asset_preview() - tagging.py - apply_tags(), remove_tags(), list_tags() Removed from queries/asset_info.py: - ingest_fs_asset (moved to services/ingest.py as ingest_file_from_path) - update_asset_info_full (moved to services/asset_management.py as update_asset_metadata) - create_asset_info_for_existing_asset (moved to services/ingest.py as register_existing_asset) Updated manager.py: - Now a thin adapter that transforms API schemas to/from service calls - Delegates all business logic to services layer - No longer imports sqlalchemy.orm.Session or models directly Test updates: - Fixed test_cache_state.py import of pick_best_live_path (moved to helpers.py) - Added comprehensive service layer tests (41 new tests) - All 112 query + service tests pass Amp-Thread-ID: https://ampcode.com/threads/T-019c24e2-7ae4-707f-ad19-c775ed8b82b5 Co-authored-by: Amp <amp@ampcode.com>
75 lines
1.9 KiB
Python
75 lines
1.9 KiB
Python
import sqlalchemy as sa
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.assets.database.models import Asset
|
|
|
|
|
|
def asset_exists_by_hash(
|
|
session: Session,
|
|
*,
|
|
asset_hash: str,
|
|
) -> bool:
|
|
"""
|
|
Check if an asset with a given hash exists in database.
|
|
"""
|
|
row = (
|
|
session.execute(
|
|
select(sa.literal(True)).select_from(Asset).where(Asset.hash == asset_hash).limit(1)
|
|
)
|
|
).first()
|
|
return row is not None
|
|
|
|
|
|
def get_asset_by_hash(
|
|
session: Session,
|
|
*,
|
|
asset_hash: str,
|
|
) -> Asset | None:
|
|
return (
|
|
session.execute(select(Asset).where(Asset.hash == asset_hash).limit(1))
|
|
).scalars().first()
|
|
|
|
|
|
def upsert_asset(
|
|
session: Session,
|
|
*,
|
|
asset_hash: str,
|
|
size_bytes: int,
|
|
mime_type: str | None = None,
|
|
) -> tuple[Asset, bool, bool]:
|
|
"""Upsert an Asset by hash. Returns (asset, created, updated)."""
|
|
from sqlalchemy.dialects import sqlite
|
|
|
|
vals = {"hash": asset_hash, "size_bytes": int(size_bytes)}
|
|
if mime_type:
|
|
vals["mime_type"] = mime_type
|
|
|
|
ins = (
|
|
sqlite.insert(Asset)
|
|
.values(**vals)
|
|
.on_conflict_do_nothing(index_elements=[Asset.hash])
|
|
)
|
|
res = session.execute(ins)
|
|
created = int(res.rowcount or 0) > 0
|
|
|
|
asset = session.execute(
|
|
select(Asset).where(Asset.hash == asset_hash).limit(1)
|
|
).scalars().first()
|
|
if not asset:
|
|
raise RuntimeError("Asset row not found after upsert.")
|
|
|
|
updated = False
|
|
if not created:
|
|
changed = False
|
|
if asset.size_bytes != int(size_bytes) and int(size_bytes) > 0:
|
|
asset.size_bytes = int(size_bytes)
|
|
changed = True
|
|
if mime_type and asset.mime_type != mime_type:
|
|
asset.mime_type = mime_type
|
|
changed = True
|
|
if changed:
|
|
updated = True
|
|
|
|
return asset, created, updated
|