mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-12-24 21:40:50 +08:00
add Get Asset endpoint
This commit is contained in:
parent
6fade5da38
commit
7c1b0be496
@ -49,7 +49,7 @@ async def list_assets(request: web.Request) -> web.Response:
|
||||
return web.json_response(payload.model_dump(mode="json"))
|
||||
|
||||
|
||||
@ROUTES.get("/api/assets/{id}/content")
|
||||
@ROUTES.get("/api/assets/{id:\\d+}/content")
|
||||
async def download_asset_content(request: web.Request) -> web.Response:
|
||||
asset_info_id_raw = request.match_info.get("id")
|
||||
try:
|
||||
@ -198,7 +198,24 @@ async def upload_asset(request: web.Request) -> web.Response:
|
||||
return _error_response(500, "INTERNAL", "Unexpected server error.")
|
||||
|
||||
|
||||
@ROUTES.put("/api/assets/{id}")
|
||||
@ROUTES.get("/api/assets/{id:\\d+}")
|
||||
async def get_asset(request: web.Request) -> web.Response:
|
||||
asset_info_id_raw = request.match_info.get("id")
|
||||
try:
|
||||
asset_info_id = int(asset_info_id_raw)
|
||||
except Exception:
|
||||
return _error_response(400, "INVALID_ID", f"AssetInfo id '{asset_info_id_raw}' is not a valid integer.")
|
||||
|
||||
try:
|
||||
result = await assets_manager.get_asset(asset_info_id=asset_info_id)
|
||||
except ValueError as ve:
|
||||
return _error_response(404, "ASSET_NOT_FOUND", str(ve), {"id": asset_info_id})
|
||||
except Exception:
|
||||
return _error_response(500, "INTERNAL", "Unexpected server error.")
|
||||
return web.json_response(result.model_dump(mode="json"), status=200)
|
||||
|
||||
|
||||
@ROUTES.put("/api/assets/{id:\\d+}")
|
||||
async def update_asset(request: web.Request) -> web.Response:
|
||||
asset_info_id_raw = request.match_info.get("id")
|
||||
try:
|
||||
@ -227,7 +244,7 @@ async def update_asset(request: web.Request) -> web.Response:
|
||||
return web.json_response(result.model_dump(mode="json"), status=200)
|
||||
|
||||
|
||||
@ROUTES.delete("/api/assets/{id}")
|
||||
@ROUTES.delete("/api/assets/{id:\\d+}")
|
||||
async def delete_asset(request: web.Request) -> web.Response:
|
||||
asset_info_id_raw = request.match_info.get("id")
|
||||
try:
|
||||
@ -267,7 +284,7 @@ async def get_tags(request: web.Request) -> web.Response:
|
||||
return web.json_response(result.model_dump(mode="json"))
|
||||
|
||||
|
||||
@ROUTES.post("/api/assets/{id}/tags")
|
||||
@ROUTES.post("/api/assets/{id:\\d+}/tags")
|
||||
async def add_asset_tags(request: web.Request) -> web.Response:
|
||||
asset_info_id_raw = request.match_info.get("id")
|
||||
try:
|
||||
@ -298,7 +315,7 @@ async def add_asset_tags(request: web.Request) -> web.Response:
|
||||
return web.json_response(result.model_dump(mode="json"), status=200)
|
||||
|
||||
|
||||
@ROUTES.delete("/api/assets/{id}/tags")
|
||||
@ROUTES.delete("/api/assets/{id:\\d+}/tags")
|
||||
async def delete_asset_tags(request: web.Request) -> web.Response:
|
||||
asset_info_id_raw = request.match_info.get("id")
|
||||
try:
|
||||
|
||||
@ -43,7 +43,7 @@ class AssetUpdated(BaseModel):
|
||||
return v.isoformat() if v else None
|
||||
|
||||
|
||||
class AssetCreated(BaseModel):
|
||||
class AssetDetail(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
asset_hash: str
|
||||
@ -54,7 +54,6 @@ class AssetCreated(BaseModel):
|
||||
preview_hash: Optional[str] = None
|
||||
created_at: Optional[datetime] = None
|
||||
last_access_time: Optional[datetime] = None
|
||||
created_new: bool
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@ -63,6 +62,10 @@ class AssetCreated(BaseModel):
|
||||
return v.isoformat() if v else None
|
||||
|
||||
|
||||
class AssetCreated(AssetDetail):
|
||||
created_new: bool
|
||||
|
||||
|
||||
class TagUsage(BaseModel):
|
||||
name: str
|
||||
count: int
|
||||
|
||||
@ -24,6 +24,7 @@ from .database.services import (
|
||||
asset_exists_by_hash,
|
||||
get_asset_by_hash,
|
||||
create_asset_info_for_existing_asset,
|
||||
fetch_asset_info_asset_and_tags,
|
||||
)
|
||||
from .api import schemas_in, schemas_out
|
||||
from ._assets_helpers import get_name_and_tags_from_asset_path, ensure_within_base, resolve_destination_from_tags
|
||||
@ -140,6 +141,27 @@ async def list_assets(
|
||||
)
|
||||
|
||||
|
||||
async def get_asset(*, asset_info_id: int) -> schemas_out.AssetDetail:
|
||||
async with await create_session() as session:
|
||||
res = await fetch_asset_info_asset_and_tags(session, asset_info_id=asset_info_id)
|
||||
if not res:
|
||||
raise ValueError(f"AssetInfo {asset_info_id} not found")
|
||||
info, asset, tag_names = res
|
||||
|
||||
return schemas_out.AssetDetail(
|
||||
id=info.id,
|
||||
name=info.name,
|
||||
asset_hash=info.asset_hash,
|
||||
size=int(asset.size_bytes) if asset and asset.size_bytes is not None else None,
|
||||
mime_type=asset.mime_type if asset else None,
|
||||
tags=tag_names,
|
||||
preview_hash=info.preview_hash,
|
||||
user_metadata=info.user_metadata or {},
|
||||
created_at=info.created_at,
|
||||
last_access_time=info.last_access_time,
|
||||
)
|
||||
|
||||
|
||||
async def resolve_asset_content_for_download(
|
||||
*, asset_info_id: int
|
||||
) -> tuple[str, str, str]:
|
||||
|
||||
@ -9,7 +9,7 @@ from typing import Any, Sequence, Optional, Iterable
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete, exists, func
|
||||
from sqlalchemy.orm import contains_eager
|
||||
from sqlalchemy.orm import contains_eager, noload
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from .models import Asset, AssetInfo, AssetInfoTag, AssetCacheState, Tag, AssetInfoMeta, AssetLocation
|
||||
@ -407,6 +407,41 @@ async def fetch_asset_info_and_asset(session: AsyncSession, *, asset_info_id: in
|
||||
return pair[0], pair[1]
|
||||
|
||||
|
||||
async def fetch_asset_info_asset_and_tags(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
asset_info_id: int,
|
||||
) -> Optional[tuple[AssetInfo, Asset, list[str]]]:
|
||||
"""Fetch AssetInfo, its Asset, and all tag names.
|
||||
|
||||
Returns:
|
||||
(AssetInfo, Asset, [tag_names]) or None if the asset_info_id does not exist.
|
||||
"""
|
||||
stmt = (
|
||||
select(AssetInfo, Asset, Tag.name)
|
||||
.join(Asset, Asset.hash == AssetInfo.asset_hash)
|
||||
.join(AssetInfoTag, AssetInfoTag.asset_info_id == AssetInfo.id, isouter=True)
|
||||
.join(Tag, Tag.name == AssetInfoTag.tag_name, isouter=True)
|
||||
.where(AssetInfo.id == asset_info_id)
|
||||
.options(noload(AssetInfo.tags))
|
||||
.order_by(Tag.name.asc())
|
||||
)
|
||||
|
||||
rows = (await session.execute(stmt)).all()
|
||||
if not rows:
|
||||
return None
|
||||
|
||||
# First row contains the mapped entities; tags may repeat across rows
|
||||
first_info, first_asset, _ = rows[0]
|
||||
tags: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for _info, _asset, tag_name in rows:
|
||||
if tag_name and tag_name not in seen:
|
||||
seen.add(tag_name)
|
||||
tags.append(tag_name)
|
||||
return first_info, first_asset, tags
|
||||
|
||||
|
||||
async def get_cache_state_by_asset_hash(session: AsyncSession, *, asset_hash: str) -> Optional[AssetCacheState]:
|
||||
return await session.get(AssetCacheState, asset_hash)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user