diff --git a/invokeai/app/api/models/images.py b/invokeai/app/api/models/images.py index c0f2b152c7..866e181561 100644 --- a/invokeai/app/api/models/images.py +++ b/invokeai/app/api/models/images.py @@ -34,8 +34,7 @@ class ProgressImage(BaseModel): dataURL: str = Field(description="The image data as a b64 data URL") -class NonNullableImageField(BaseModel): - """Non-nullable ImageField, used for delete_images route""" - - image_type: ImageType - image_name: str +class SavedImage(BaseModel): + image_name: str = Field(description="The name of the saved image") + thumbnail_name: str = Field(description="The name of the saved thumbnail") + created: int = Field(description="The created timestamp of the saved image") diff --git a/invokeai/app/api/routers/images.py b/invokeai/app/api/routers/images.py index 2eb64cfd6b..22c07033cf 100644 --- a/invokeai/app/api/routers/images.py +++ b/invokeai/app/api/routers/images.py @@ -13,7 +13,6 @@ from PIL import Image from invokeai.app.api.models.images import ( ImageResponse, ImageResponseMetadata, - NonNullableImageField, ) from invokeai.app.services.item_storage import PaginatedResults @@ -99,7 +98,7 @@ async def upload_image( filename = f"{uuid.uuid4()}_{str(int(datetime.now(timezone.utc).timestamp()))}.png" - (image_name, thumbnail_name, ctime) = ApiDependencies.invoker.services.images.save( + saved_image = ApiDependencies.invoker.services.images.save( ImageType.UPLOAD, filename, img ) @@ -107,17 +106,17 @@ async def upload_image( res = ImageResponse( image_type=ImageType.UPLOAD, - image_name=image_name, + image_name=saved_image.image_name, image_url=request.url_for( - "get_image", image_type=ImageType.UPLOAD.value, image_name=image_name + "get_image", image_type=ImageType.UPLOAD.value, image_name=saved_image.image_name ), thumbnail_url=request.url_for( "get_thumbnail", thumbnail_type=ImageType.UPLOAD.value, - thumbnail_name=thumbnail_name, + thumbnail_name=saved_image.thumbnail_name, ), metadata=ImageResponseMetadata( - created=ctime, + created=saved_image.created, width=img.width, height=img.height, invokeai=invokeai_metadata, @@ -147,20 +146,3 @@ async def list_images( """Gets a list of images""" result = ApiDependencies.invoker.services.images.list(image_type, page, per_page) return result - - -@images_router.delete( - "/", - operation_id="delete_images", -) -async def delete_images( - images: list[NonNullableImageField] = Body( - description="The list of images to delete" - ), -) -> None: - """Deletes a list of images and their thumbnails""" - - for i in images: - ApiDependencies.invoker.services.images.delete( - image_type=i.image_type, image_name=i.image_name - ) diff --git a/invokeai/app/api/routers/sessions.py b/invokeai/app/api/routers/sessions.py index 09ffa3a60c..da842a3968 100644 --- a/invokeai/app/api/routers/sessions.py +++ b/invokeai/app/api/routers/sessions.py @@ -2,7 +2,7 @@ from typing import Annotated, List, Optional, Union -from fastapi import Body, HTTPException, Path, Query +from fastapi import Body, HTTPException, Path, Query, Response from fastapi.routing import APIRouter from pydantic.fields import Field @@ -258,7 +258,7 @@ async def invoke_session( all: bool = Query( default=False, description="Whether or not to invoke all remaining invocations" ), -) -> None: +) -> Response: """Invokes a session""" session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id) if session is None: @@ -268,7 +268,7 @@ async def invoke_session( raise HTTPException(status_code=400) ApiDependencies.invoker.invoke(session, invoke_all=all) - raise HTTPException(status_code=202) + return Response(status_code=202) @session_router.delete( @@ -280,7 +280,7 @@ async def invoke_session( ) async def cancel_session_invoke( session_id: str = Path(description="The id of the session to cancel"), -) -> None: +) -> Response: """Invokes a session""" ApiDependencies.invoker.cancel(session_id) - raise HTTPException(status_code=202) + return Response(status_code=202) diff --git a/invokeai/app/services/image_storage.py b/invokeai/app/services/image_storage.py index af40bf47ca..cd2c8a13a3 100644 --- a/invokeai/app/services/image_storage.py +++ b/invokeai/app/services/image_storage.py @@ -9,7 +9,11 @@ from typing import Dict, List, Tuple from PIL.Image import Image import PIL.Image as PILImage -from invokeai.app.api.models.images import ImageResponse, ImageResponseMetadata +from invokeai.app.api.models.images import ( + ImageResponse, + ImageResponseMetadata, + SavedImage, +) from invokeai.app.models.image import ImageType from invokeai.app.services.metadata import ( InvokeAIMetadata, @@ -57,7 +61,7 @@ class ImageStorageBase(ABC): image_name: str, image: Image, metadata: InvokeAIMetadata | None = None, - ) -> Tuple[str, str, int]: + ) -> SavedImage: """Saves an image and a 256x256 WEBP thumbnail. Returns a tuple of the image name, thumbnail name, and created timestamp.""" pass @@ -189,7 +193,7 @@ class DiskImageStorage(ImageStorageBase): image_name: str, image: Image, metadata: InvokeAIMetadata | None = None, - ) -> Tuple[str, str, int]: + ) -> SavedImage: image_path = self.get_path(image_type, image_name) # TODO: Reading the image and then saving it strips the metadata... @@ -207,7 +211,11 @@ class DiskImageStorage(ImageStorageBase): self.__set_cache(image_path, image) self.__set_cache(thumbnail_path, thumbnail_image) - return (image_name, thumbnail_name, int(os.path.getctime(image_path))) + return SavedImage( + image_name=image_name, + thumbnail_name=thumbnail_name, + created=int(os.path.getctime(image_path)), + ) def delete(self, image_type: ImageType, image_name: str) -> None: basename = os.path.basename(image_name) @@ -218,7 +226,7 @@ class DiskImageStorage(ImageStorageBase): if image_path in self.__cache: del self.__cache[image_path] - thumbnail_name = f"{os.path.splitext(basename)[0]}.webp" + thumbnail_name = get_thumbnail_name(image_name) thumbnail_path = self.get_path(image_type, thumbnail_name, True) if os.path.exists(image_path):