mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-02 18:20:51 +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"))
|
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:
|
async def download_asset_content(request: web.Request) -> web.Response:
|
||||||
asset_info_id_raw = request.match_info.get("id")
|
asset_info_id_raw = request.match_info.get("id")
|
||||||
try:
|
try:
|
||||||
@ -198,7 +198,24 @@ async def upload_asset(request: web.Request) -> web.Response:
|
|||||||
return _error_response(500, "INTERNAL", "Unexpected server error.")
|
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:
|
async def update_asset(request: web.Request) -> web.Response:
|
||||||
asset_info_id_raw = request.match_info.get("id")
|
asset_info_id_raw = request.match_info.get("id")
|
||||||
try:
|
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)
|
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:
|
async def delete_asset(request: web.Request) -> web.Response:
|
||||||
asset_info_id_raw = request.match_info.get("id")
|
asset_info_id_raw = request.match_info.get("id")
|
||||||
try:
|
try:
|
||||||
@ -267,7 +284,7 @@ async def get_tags(request: web.Request) -> web.Response:
|
|||||||
return web.json_response(result.model_dump(mode="json"))
|
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:
|
async def add_asset_tags(request: web.Request) -> web.Response:
|
||||||
asset_info_id_raw = request.match_info.get("id")
|
asset_info_id_raw = request.match_info.get("id")
|
||||||
try:
|
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)
|
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:
|
async def delete_asset_tags(request: web.Request) -> web.Response:
|
||||||
asset_info_id_raw = request.match_info.get("id")
|
asset_info_id_raw = request.match_info.get("id")
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -43,7 +43,7 @@ class AssetUpdated(BaseModel):
|
|||||||
return v.isoformat() if v else None
|
return v.isoformat() if v else None
|
||||||
|
|
||||||
|
|
||||||
class AssetCreated(BaseModel):
|
class AssetDetail(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
name: str
|
name: str
|
||||||
asset_hash: str
|
asset_hash: str
|
||||||
@ -54,7 +54,6 @@ class AssetCreated(BaseModel):
|
|||||||
preview_hash: Optional[str] = None
|
preview_hash: Optional[str] = None
|
||||||
created_at: Optional[datetime] = None
|
created_at: Optional[datetime] = None
|
||||||
last_access_time: Optional[datetime] = None
|
last_access_time: Optional[datetime] = None
|
||||||
created_new: bool
|
|
||||||
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
@ -63,6 +62,10 @@ class AssetCreated(BaseModel):
|
|||||||
return v.isoformat() if v else None
|
return v.isoformat() if v else None
|
||||||
|
|
||||||
|
|
||||||
|
class AssetCreated(AssetDetail):
|
||||||
|
created_new: bool
|
||||||
|
|
||||||
|
|
||||||
class TagUsage(BaseModel):
|
class TagUsage(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
count: int
|
count: int
|
||||||
|
|||||||
@ -24,6 +24,7 @@ from .database.services import (
|
|||||||
asset_exists_by_hash,
|
asset_exists_by_hash,
|
||||||
get_asset_by_hash,
|
get_asset_by_hash,
|
||||||
create_asset_info_for_existing_asset,
|
create_asset_info_for_existing_asset,
|
||||||
|
fetch_asset_info_asset_and_tags,
|
||||||
)
|
)
|
||||||
from .api import schemas_in, schemas_out
|
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
|
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(
|
async def resolve_asset_content_for_download(
|
||||||
*, asset_info_id: int
|
*, asset_info_id: int
|
||||||
) -> tuple[str, str, str]:
|
) -> tuple[str, str, str]:
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from typing import Any, Sequence, Optional, Iterable
|
|||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select, delete, exists, func
|
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 sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from .models import Asset, AssetInfo, AssetInfoTag, AssetCacheState, Tag, AssetInfoMeta, AssetLocation
|
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]
|
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]:
|
async def get_cache_state_by_asset_hash(session: AsyncSession, *, asset_hash: str) -> Optional[AssetCacheState]:
|
||||||
return await session.get(AssetCacheState, asset_hash)
|
return await session.get(AssetCacheState, asset_hash)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user