From b9375186a5975aacb1b1b863fd2134397f6ad851 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 22 May 2023 12:46:37 +1000 Subject: [PATCH] feat(nodes): consolidate image routers --- invokeai/app/api/routers/image_files.py | 47 ------ invokeai/app/api/routers/image_records.py | 71 --------- invokeai/app/api/routers/images.py | 143 ++++++++++++++++--- invokeai/app/api_app.py | 6 +- invokeai/app/services/models/image_record.py | 12 +- invokeai/app/services/urls.py | 4 +- 6 files changed, 137 insertions(+), 146 deletions(-) delete mode 100644 invokeai/app/api/routers/image_files.py delete mode 100644 invokeai/app/api/routers/image_records.py diff --git a/invokeai/app/api/routers/image_files.py b/invokeai/app/api/routers/image_files.py deleted file mode 100644 index 2694df5b19..0000000000 --- a/invokeai/app/api/routers/image_files.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654) and the InvokeAI Team -from fastapi import HTTPException, Path -from fastapi.responses import FileResponse -from fastapi.routing import APIRouter -from invokeai.app.models.image import ImageType - -from ..dependencies import ApiDependencies - -image_files_router = APIRouter(prefix="/v1/files/images", tags=["images", "files"]) - - -@image_files_router.get("/{image_type}/{image_name}", operation_id="get_image") -async def get_image( - image_type: ImageType = Path(description="The type of the image to get"), - image_name: str = Path(description="The id of the image to get"), -) -> FileResponse: - """Gets an image""" - - try: - path = ApiDependencies.invoker.services.images_new.get_path( - image_type=image_type, image_name=image_name - ) - - return FileResponse(path) - except Exception as e: - raise HTTPException(status_code=404) - - -@image_files_router.get( - "/{image_type}/{image_name}/thumbnail", operation_id="get_thumbnail" -) -async def get_thumbnail( - image_type: ImageType = Path( - description="The type of the image whose thumbnail to get" - ), - image_name: str = Path(description="The id of the image whose thumbnail to get"), -) -> FileResponse: - """Gets a thumbnail""" - - try: - path = ApiDependencies.invoker.services.images_new.get_path( - image_type=image_type, image_name=image_name, thumbnail=True - ) - - return FileResponse(path) - except Exception as e: - raise HTTPException(status_code=404) diff --git a/invokeai/app/api/routers/image_records.py b/invokeai/app/api/routers/image_records.py deleted file mode 100644 index 5dccccae41..0000000000 --- a/invokeai/app/api/routers/image_records.py +++ /dev/null @@ -1,71 +0,0 @@ -from fastapi import HTTPException, Path, Query -from fastapi.routing import APIRouter -from invokeai.app.models.image import ( - ImageCategory, - ImageType, -) -from invokeai.app.services.item_storage import PaginatedResults -from invokeai.app.services.models.image_record import ImageDTO - -from ..dependencies import ApiDependencies - -image_records_router = APIRouter( - prefix="/v1/images/records", tags=["images", "records"] -) - - -@image_records_router.get("/{image_type}/{image_name}", operation_id="get_image_record") -async def get_image_record( - image_type: ImageType = Path(description="The type of the image record to get"), - image_name: str = Path(description="The id of the image record to get"), -) -> ImageDTO: - """Gets an image record by id""" - - try: - return ApiDependencies.invoker.services.images_new.get_dto( - image_type=image_type, image_name=image_name - ) - except Exception as e: - raise HTTPException(status_code=404) - - -@image_records_router.get( - "/", - operation_id="list_image_records", -) -async def list_image_records( - image_type: ImageType = Query(description="The type of image records to get"), - image_category: ImageCategory = Query( - description="The kind of image records to get" - ), - page: int = Query(default=0, description="The page of image records to get"), - per_page: int = Query( - default=10, description="The number of image records per page" - ), -) -> PaginatedResults[ImageDTO]: - """Gets a list of image records by type and category""" - - image_dtos = ApiDependencies.invoker.services.images_new.get_many( - image_type=image_type, - image_category=image_category, - page=page, - per_page=per_page, - ) - - return image_dtos - - -@image_records_router.delete("/{image_type}/{image_name}", operation_id="delete_image") -async def delete_image_record( - image_type: ImageType = Query(description="The type of image to delete"), - image_name: str = Path(description="The name of the image to delete"), -) -> None: - """Deletes an image record""" - - try: - ApiDependencies.invoker.services.images_new.delete( - image_type=image_type, image_name=image_name - ) - except Exception as e: - # TODO: Does this need any exception handling at all? - pass diff --git a/invokeai/app/api/routers/images.py b/invokeai/app/api/routers/images.py index c38d99c74f..18b47b5595 100644 --- a/invokeai/app/api/routers/images.py +++ b/invokeai/app/api/routers/images.py @@ -1,15 +1,13 @@ import io -import uuid from fastapi import HTTPException, Path, Query, Request, Response, UploadFile from fastapi.routing import APIRouter +from fastapi.responses import FileResponse from PIL import Image from invokeai.app.models.image import ( ImageCategory, ImageType, ) -from invokeai.app.services.image_record_storage import ImageRecordStorageBase -from invokeai.app.services.image_file_storage import ImageFileStorageBase -from invokeai.app.services.models.image_record import ImageRecord +from invokeai.app.services.models.image_record import ImageDTO, ImageUrlsDTO from invokeai.app.services.item_storage import PaginatedResults from ..dependencies import ApiDependencies @@ -32,7 +30,7 @@ async def upload_image( request: Request, response: Response, image_category: ImageCategory = ImageCategory.IMAGE, -) -> ImageRecord: +) -> ImageDTO: """Uploads an image""" if not file.content_type.startswith("image"): raise HTTPException(status_code=415, detail="Not an image") @@ -40,38 +38,145 @@ async def upload_image( contents = await file.read() try: - img = Image.open(io.BytesIO(contents)) + pil_image = Image.open(io.BytesIO(contents)) except: # Error opening the image raise HTTPException(status_code=415, detail="Failed to read image") try: - image_record = ApiDependencies.invoker.services.images_new.create( - image=img, - image_type=image_type, - image_category=image_category, + image_dto = ApiDependencies.invoker.services.images_new.create( + pil_image, + image_type, + image_category, ) response.status_code = 201 - response.headers["Location"] = image_record.image_url + response.headers["Location"] = image_dto.image_url - return image_record + return image_dto except Exception as e: - raise HTTPException(status_code=500) - + raise HTTPException(status_code=500, detail="Failed to create image") @images_router.delete("/{image_type}/{image_name}", operation_id="delete_image") -async def delete_image_record( +async def delete_image( image_type: ImageType = Query(description="The type of image to delete"), image_name: str = Path(description="The name of the image to delete"), ) -> None: - """Deletes an image record""" + """Deletes an image""" try: - ApiDependencies.invoker.services.images_new.delete( - image_type=image_type, image_name=image_name - ) + ApiDependencies.invoker.services.images_new.delete(image_type, image_name) except Exception as e: # TODO: Does this need any exception handling at all? pass + + +@images_router.get( + "/{image_type}/{image_name}/record", + operation_id="get_image_record", + response_model=ImageDTO, +) +async def get_image_record( + image_type: ImageType = Path(description="The type of the image record to get"), + image_name: str = Path(description="The id of the image record to get"), +) -> ImageDTO: + """Gets an image record by id""" + + try: + return ApiDependencies.invoker.services.images_new.get_dto( + image_type, image_name + ) + except Exception as e: + raise HTTPException(status_code=404) + + +@images_router.get("/{image_type}/{image_name}/image", operation_id="get_image") +async def get_image( + image_type: ImageType = Path(description="The type of the image to get"), + image_name: str = Path(description="The id of the image to get"), +) -> FileResponse: + """Gets an image""" + + try: + path = ApiDependencies.invoker.services.images_new.get_path( + image_type, image_name + ) + + return FileResponse(path) + except Exception as e: + raise HTTPException(status_code=404) + + +@images_router.get("/{image_type}/{image_name}/thumbnail", operation_id="get_thumbnail") +async def get_thumbnail( + image_type: ImageType = Path( + description="The type of the image whose thumbnail to get" + ), + image_name: str = Path(description="The id of the image whose thumbnail to get"), +) -> FileResponse: + """Gets a thumbnail""" + + try: + path = ApiDependencies.invoker.services.images_new.get_path( + image_type, image_name, thumbnail=True + ) + + return FileResponse(path) + except Exception as e: + raise HTTPException(status_code=404) + + +@images_router.get( + "/{image_type}/{image_name}/urls", + operation_id="get_image_urls", + response_model=ImageUrlsDTO, +) +async def get_image_urls( + image_type: ImageType = Path(description="The type of the image whose URL to get"), + image_name: str = Path(description="The id of the image whose URL to get"), +) -> ImageUrlsDTO: + """Gets an image and thumbnail URL""" + + try: + image_url = ApiDependencies.invoker.services.images_new.get_url( + image_type, image_name + ) + thumbnail_url = ApiDependencies.invoker.services.images_new.get_url( + image_type, image_name, thumbnail=True + ) + return ImageUrlsDTO( + image_type=image_type, + image_name=image_name, + image_url=image_url, + thumbnail_url=thumbnail_url, + ) + except Exception as e: + raise HTTPException(status_code=404) + + +@images_router.get( + "/", + operation_id="list_image_records", + response_model=PaginatedResults[ImageDTO], +) +async def list_image_records( + image_type: ImageType = Query(description="The type of image records to get"), + image_category: ImageCategory = Query( + description="The kind of image records to get" + ), + page: int = Query(default=0, description="The page of image records to get"), + per_page: int = Query( + default=10, description="The number of image records per page" + ), +) -> PaginatedResults[ImageDTO]: + """Gets a list of image records by type and category""" + + image_dtos = ApiDependencies.invoker.services.images_new.get_many( + image_type, + image_category, + page, + per_page, + ) + + return image_dtos diff --git a/invokeai/app/api_app.py b/invokeai/app/api_app.py index aaef1d78be..964202786a 100644 --- a/invokeai/app/api_app.py +++ b/invokeai/app/api_app.py @@ -15,7 +15,7 @@ from fastapi_events.middleware import EventHandlerASGIMiddleware from pydantic.schema import schema from .api.dependencies import ApiDependencies -from .api.routers import image_files, image_records, sessions, models, images +from .api.routers import sessions, models, images from .api.sockets import SocketIO from .invocations.baseinvocation import BaseInvocation from .services.config import InvokeAIAppConfig @@ -73,10 +73,6 @@ app.include_router(sessions.session_router, prefix="/api") app.include_router(models.models_router, prefix="/api") -app.include_router(image_files.image_files_router, prefix="/api") - -app.include_router(image_records.image_records_router, prefix="/api") - app.include_router(images.images_router, prefix="/api") # Build a custom OpenAPI to include all outputs diff --git a/invokeai/app/services/models/image_record.py b/invokeai/app/services/models/image_record.py index cd2f3aacbc..6e15574eb9 100644 --- a/invokeai/app/services/models/image_record.py +++ b/invokeai/app/services/models/image_record.py @@ -23,13 +23,21 @@ class ImageRecord(BaseModel): ) -class ImageDTO(ImageRecord): - """Deserialized image record with URLs.""" +class ImageUrlsDTO(BaseModel): + """The URLs for an image and its thumbnaill""" + image_name: str = Field(description="The name of the image.") + image_type: ImageType = Field(description="The type of the image.") image_url: str = Field(description="The URL of the image.") thumbnail_url: str = Field(description="The thumbnail URL of the image.") +class ImageDTO(ImageRecord, ImageUrlsDTO): + """Deserialized image record with URLs.""" + + pass + + def image_record_to_dto( image_record: ImageRecord, image_url: str, thumbnail_url: str ) -> ImageDTO: diff --git a/invokeai/app/services/urls.py b/invokeai/app/services/urls.py index 989f6853c2..0e2389b7d0 100644 --- a/invokeai/app/services/urls.py +++ b/invokeai/app/services/urls.py @@ -25,6 +25,6 @@ class LocalUrlService(UrlServiceBase): ) -> str: image_basename = os.path.basename(image_name) if thumbnail: - return f"{self._base_url}/files/images/{image_type.value}/{image_basename}/thumbnail" + return f"{self._base_url}/images/{image_type.value}/{image_basename}/thumbnail" - return f"{self._base_url}/files/images/{image_type.value}/{image_basename}" + return f"{self._base_url}/images/{image_type.value}/{image_basename}/image"