From 388fcb9fdab91fe4aaafc7111dac430c6cfc5abd Mon Sep 17 00:00:00 2001 From: kundan-krishna366 Date: Fri, 20 Mar 2026 14:38:58 +0530 Subject: [PATCH 1/3] feat(backend): add native HEIC support and fix EXIF orientation --- apps/web/backend/app/main.py | 21 ++++++-- apps/web/backend/pyproject.toml | 4 ++ apps/web/backend/uv.lock | 85 +++++++++++++++++++++++++++++---- 3 files changed, 97 insertions(+), 13 deletions(-) diff --git a/apps/web/backend/app/main.py b/apps/web/backend/app/main.py index d6d818a..8878057 100644 --- a/apps/web/backend/app/main.py +++ b/apps/web/backend/app/main.py @@ -9,9 +9,13 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import Response, JSONResponse, FileResponse from fastapi.staticfiles import StaticFiles -from PIL import Image +from PIL import Image, ImageOps +import pillow_heif # Required for HEIF/HEIC decoding import uvicorn +# Enable HEIF support globally for Pillow +pillow_heif.register_heif_opener() + # Import withoutbg package (install via: uv sync or pip install -e ../../../packages/python) from withoutbg import WithoutBG, __version__ from withoutbg.exceptions import WithoutBGError @@ -80,13 +84,20 @@ async def remove_background_endpoint( Processed image with background removed """ try: - # Validate file type - if not file.content_type or not file.content_type.startswith("image/"): - raise HTTPException(status_code=400, detail="File must be an image") + # Support standard image types and native Apple HEIC/HEIF + is_image = file.content_type and file.content_type.startswith("image/") + is_heic = file.filename and file.filename.lower().endswith((".heic", ".heif")) + + if not (is_image or is_heic): + raise HTTPException(status_code=400, detail="File must be an image (JPEG, PNG, or HEIC)") # Read uploaded file contents = await file.read() - input_image = Image.open(io.BytesIO(contents)) + raw_image = Image.open(io.BytesIO(contents)) + + # 1. Apply EXIF orientation (prevents rotated mobile uploads) + # 2. Force RGBA for consistency across inference models + input_image = ImageOps.exif_transpose(raw_image).convert("RGBA") # Process image using appropriate model if api_key: diff --git a/apps/web/backend/pyproject.toml b/apps/web/backend/pyproject.toml index 559e077..09a5eb7 100644 --- a/apps/web/backend/pyproject.toml +++ b/apps/web/backend/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "uvicorn[standard]>=0.32.0", "python-multipart>=0.0.12", "pillow>=8.0.0", + "pillow-heif>=1.3.0", ] [build-system] @@ -17,3 +18,6 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["app"] + +[tool.uv.sources] +withoutbg = { path = "../../../packages/python", editable = true } diff --git a/apps/web/backend/uv.lock b/apps/web/backend/uv.lock index 55232b2..18e791b 100644 --- a/apps/web/backend/uv.lock +++ b/apps/web/backend/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.12" [[package]] @@ -482,6 +482,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, ] +[[package]] +name = "pillow-heif" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/58/2df4fc42840633e01c97b75965cb1bc6e14425973b92382391650e97e4b7/pillow_heif-1.3.0.tar.gz", hash = "sha256:af8d2bda85e395677d5bb50d7bda3b5655c946cc95b913b5e7222fabacbb467f", size = 17133211, upload-time = "2026-02-27T12:21:36.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/f7/e0b13500470421536fdfe01acfc4c56daccd3d23655605aa04cfb30cc58c/pillow_heif-1.3.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:079abbcaeb42ef0849a33f35c1a96ccd431feb56b242a0d4f8435a1c8ca02c7d", size = 4667382, upload-time = "2026-02-27T12:20:45.149Z" }, + { url = "https://files.pythonhosted.org/packages/a6/fb/beb62f26231e7c76d235e993d18b4ddb3d2d427e93539b8e6eb8dc188fa7/pillow_heif-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76c33f80ec111492642b98309db98516a7fba9677dcda9ec5fe9111b7e38d720", size = 3392733, upload-time = "2026-02-27T12:20:46.606Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/3d86e237b3a20c909f62a50e8cb3492ed6206675136d1ebddb168920261b/pillow_heif-1.3.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33b838d06e2fd730f806af5a76bfc4cd3de9d146d88d37572e40f7a4c4ff8221", size = 5844247, upload-time = "2026-02-27T12:20:49.669Z" }, + { url = "https://files.pythonhosted.org/packages/58/2a/826faf3df8c9ef9a19dc96bdfff34cf76f8b025540d5f931903d2c64f25c/pillow_heif-1.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f92b387af891cf5d98f52e79eeaf51ee7955a54fe2deeec12bfb7519e41464b5", size = 5578692, upload-time = "2026-02-27T12:20:51.219Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4e/59f74fede18e3e06f98a448488df2fa4e5de1c159419d47ca345a51a0da0/pillow_heif-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f9f73246836f93f99343cbc3052b61d212d27e59ddf40262d494a1e3e54af31a", size = 6885928, upload-time = "2026-02-27T12:20:52.934Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e4/bce83d2f4d703f418d252b509a6c9d6de52dc7c4eedcf6a286f871dea824/pillow_heif-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84c3816742c2e49176e651895e73c555b9c3b0f3561d60230242f3be0c9d272b", size = 6511151, upload-time = "2026-02-27T12:20:54.236Z" }, + { url = "https://files.pythonhosted.org/packages/41/c2/87d433a9681c79e0926d8a113ea153d592ec49d1d7aa7278ee798bc490f2/pillow_heif-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:5f0db0bf49162fb1d73d13340a9576b3a2805bde026a9a40038bcc1a0878d710", size = 5483578, upload-time = "2026-02-27T12:20:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/81/c3/9effa6ab5c2c2ffb80228143c578a9a2a8e2f059dd9d067ec6ff6f6c89db/pillow_heif-1.3.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:641c50a064aa9ad6626a6b2b914b65855202f937d573d53838e344feb2e8c6d1", size = 4667379, upload-time = "2026-02-27T12:20:57.561Z" }, + { url = "https://files.pythonhosted.org/packages/23/eb/b6b52e3655f366b95301f18aecd2d35487cace18d17134b80ad0f70cc1eb/pillow_heif-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9390dd7987887aa09779fbd88bbab715c732c9ad3a71d6707284035e3ca93379", size = 3392725, upload-time = "2026-02-27T12:20:59.52Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b3/b69610e9565fc8bcaf2303f412e857c0439d23cc18cf866c72a96ec6b2e6/pillow_heif-1.3.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e8444ccb330015e1db930207d269886e4b6c666121cd9e5fdad88735950b09f", size = 5844285, upload-time = "2026-02-27T12:21:00.771Z" }, + { url = "https://files.pythonhosted.org/packages/47/8c/be44f6dea425a9756ff418cb03f5ee75ed1c7dd1ff9bee1f3893b2b82da4/pillow_heif-1.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d30054ccc97ecbe5ee3fa486a505ccc33bfbb27f005ad624ddb4c17b80ddd57", size = 5578691, upload-time = "2026-02-27T12:21:02.193Z" }, + { url = "https://files.pythonhosted.org/packages/5c/74/e12d49346a39e2204b408a835b31b2fd9a5d51f97ce3a6015cf22ca09a54/pillow_heif-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dc1b9c9efdf8345d703118449ff69696d0827bdf28e3b52f82015f5714f7c23e", size = 6885923, upload-time = "2026-02-27T12:21:03.782Z" }, + { url = "https://files.pythonhosted.org/packages/80/a6/51c937a9433f5ae9c625b686ee338bdf0080a1661f7eb34daaf75424ee77/pillow_heif-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee26b2155721e7f5f7b10fa93ca2ad3be59547c5c5e5d9d50e6ea17531b81d60", size = 6511216, upload-time = "2026-02-27T12:21:05.134Z" }, + { url = "https://files.pythonhosted.org/packages/63/0a/bb8435e127f75b434166022471bbabf11c8c1fc3d48c8595fd6ab36c2785/pillow_heif-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:17ecbbadfe10ea12a65c1c12354dc1ed8ae1e5d1b7092ea753641b029f7d6f9e", size = 5483570, upload-time = "2026-02-27T12:21:06.566Z" }, + { url = "https://files.pythonhosted.org/packages/3e/17/aa056f8edb71396dd1131abcd0c6feab00097ceec89a12fc62d2dbc3ccf5/pillow_heif-1.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8267a73d3b2d07a47a96428bd8cd4c406e1637a94f29d4c16ce08b31b8e50a07", size = 4667395, upload-time = "2026-02-27T12:21:08.16Z" }, + { url = "https://files.pythonhosted.org/packages/19/1f/da50ccd271a2878d17df359301dc2f7a79ec1cbb6e92c19ccc8c6219d497/pillow_heif-1.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:36bbea7679467caa3a154db11c04f1ca2fa8591e886f06f40f7831c14b58d771", size = 3392800, upload-time = "2026-02-27T12:21:09.668Z" }, + { url = "https://files.pythonhosted.org/packages/11/bc/1f89d927c1293cf283bc5d0ae6735d268d2de9749aa6fb94342ec838a457/pillow_heif-1.3.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea3a4b2de4b6c63407af72afdac901616807c6e6a030fe77851d227bca3727a", size = 5844547, upload-time = "2026-02-27T12:21:10.826Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/d781b23f8bff125c8dd8da63d928a35e38f2b727e89582a1fd323664e968/pillow_heif-1.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05149bd26b08dae5af7a389af6db13cef4f12c7871db73d84e40a1f3c83b0142", size = 5578827, upload-time = "2026-02-27T12:21:12.06Z" }, + { url = "https://files.pythonhosted.org/packages/2a/98/8dcdaafcf9bd8b26ed0569dc93653dc20a06faef7bfbdd4ba05c091c5b60/pillow_heif-1.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f8b7a50058fc3152f42b68aa2b30601249f61aa5c6c27876af076785c7051fd9", size = 6886088, upload-time = "2026-02-27T12:21:13.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/26/93f3c8bfffb7e8fe0244bf86117235c49c23980e61320e7484c03ac836e2/pillow_heif-1.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:edb3ef437e8841475db14721f0529e600bb55c41b549ad1794a0831e28f33bac", size = 6511291, upload-time = "2026-02-27T12:21:15.354Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f9/a8c72619ec212eb2612730fa2b3068e2d4b59e0a0957c2e8418aa4cff59e/pillow_heif-1.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:bdd6695d5be0d98ae0e9a5f88fe26f1a6eca0a5b6d43d0a92a97f89fea5842f7", size = 5640949, upload-time = "2026-02-27T12:21:16.647Z" }, + { url = "https://files.pythonhosted.org/packages/b7/31/92ce30e1ada892e18a03042bd5a8414f655304a78a36790e657f14265fed/pillow_heif-1.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:65c5d05cb7f5e1eadbe9c605ae3a4dd3ef953adb33e7d809d5fb56f8a6753588", size = 4668365, upload-time = "2026-02-27T12:21:18.004Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2b/789fa3c82063a780e84de667771b8ec30bc328511855f15a83a3c77011ec/pillow_heif-1.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dc177fbdf598770cad4afa99c082a30b9d090e60c39656904338717803ae59b2", size = 3393554, upload-time = "2026-02-27T12:21:19.642Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a4/4f8075f03c1d06d7afd674e263a3f57b7b24130c39b1544555b3b03ed369/pillow_heif-1.3.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71f88d180547bb5112b56310c8c5e338d8358320a402c80afabc6b2f39eadddb", size = 5849609, upload-time = "2026-02-27T12:21:20.953Z" }, + { url = "https://files.pythonhosted.org/packages/0d/08/e33a10bc84ade1b4ec56bdc765735bbfd452513e33537df68107edc0eb86/pillow_heif-1.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9acee893186bdde6140d30a7dc6d7c928e4ad3007989764f6e54a7a517faa332", size = 5582931, upload-time = "2026-02-27T12:21:22.571Z" }, + { url = "https://files.pythonhosted.org/packages/cb/45/6afc0f29701e0c9b911b33a35760ae6e2c581fc49b431dcce22ed18abfba/pillow_heif-1.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7cf893689132bec18f0c55a505da9ebf3a8feb33dd354fe2ac050f20f4f862e0", size = 6891268, upload-time = "2026-02-27T12:21:24.021Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0a/0d6a69f76f277692555d0e687dbf3e31d03cf76fffa3ced1fea51a18c481/pillow_heif-1.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:54404c9b6f0323114527579f54cc966b47206f99d943e47d73e1091ab0b9d2ba", size = 6515405, upload-time = "2026-02-27T12:21:25.336Z" }, + { url = "https://files.pythonhosted.org/packages/39/21/716856a36c1cc30a8f1354bf6423f251b1f50851af3e13b9cf084a13d2e3/pillow_heif-1.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:18c7c35a9d98ed9eaaf2db601ee43425ebccc698801df9c008aa04e00756a22e", size = 5641581, upload-time = "2026-02-27T12:21:26.642Z" }, +] + [[package]] name = "protobuf" version = "6.33.0" @@ -924,8 +963,7 @@ wheels = [ [[package]] name = "withoutbg" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } +source = { editable = "../../../packages/python" } dependencies = [ { name = "click" }, { name = "huggingface-hub" }, @@ -935,18 +973,48 @@ dependencies = [ { name = "requests" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/45/610d3bb7821a49d4152fdf69daddfe65c41925ac1370ee4b9f5d5a1e9436/withoutbg-0.1.0.tar.gz", hash = "sha256:b5cc22f198d67986a7e68fe7500562ab552843fb43000870b65ea1efb7f55d28", size = 284216, upload-time = "2025-08-18T20:57:10.659Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/a8/b0b5a54ce7a42b3a31c6bf543067b723ef88ffae36db9caa5fae23d00900/withoutbg-0.1.0-py3-none-any.whl", hash = "sha256:fbe088cf4b72300147b50de94833e56e97ecf40eb7d42267ef88681ae5a523c0", size = 19520, upload-time = "2025-08-18T20:57:08.612Z" }, + +[package.metadata] +requires-dist = [ + { name = "black", marker = "extra == 'dev'", specifier = ">=22.0.0" }, + { name = "click", specifier = ">=8.0.0" }, + { name = "huggingface-hub", specifier = ">=0.33.5" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0.0" }, + { name = "numpy", specifier = ">=1.21.0" }, + { name = "onnxruntime", specifier = ">=1.12.0" }, + { name = "pillow", specifier = ">=8.0.0" }, + { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=2.20.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, + { name = "requests", specifier = ">=2.25.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, + { name = "tqdm", specifier = ">=4.60.0" }, + { name = "types-pillow", marker = "extra == 'dev'", specifier = ">=10.2.0" }, + { name = "types-requests", marker = "extra == 'dev'", specifier = ">=2.32.0" }, +] +provides-extras = ["dev"] + +[package.metadata.requires-dev] +dev = [ + { name = "black", specifier = ">=24.8.0" }, + { name = "hatch", specifier = ">=1.14.1" }, + { name = "mypy", specifier = ">=1.14.1" }, + { name = "pre-commit", specifier = ">=3.5.0" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-cov", specifier = ">=5.0.0" }, + { name = "ruff", specifier = ">=0.12.5" }, + { name = "types-pillow", specifier = ">=10.2.0.20240822" }, + { name = "types-requests", specifier = ">=2.32.4.20250809" }, ] [[package]] name = "withoutbg-backend" -version = "1.0.0" +version = "1.2.1" source = { editable = "." } dependencies = [ { name = "fastapi" }, { name = "pillow" }, + { name = "pillow-heif" }, { name = "python-multipart" }, { name = "uvicorn", extra = ["standard"] }, { name = "withoutbg" }, @@ -956,7 +1024,8 @@ dependencies = [ requires-dist = [ { name = "fastapi", specifier = ">=0.115.0" }, { name = "pillow", specifier = ">=8.0.0" }, + { name = "pillow-heif", specifier = ">=1.3.0" }, { name = "python-multipart", specifier = ">=0.0.12" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.32.0" }, - { name = "withoutbg" }, + { name = "withoutbg", editable = "../../../packages/python" }, ] From 886d2b7be42d6eac7774c9c509823c8a30654831 Mon Sep 17 00:00:00 2001 From: kundan-krishna366 Date: Fri, 20 Mar 2026 15:05:56 +0530 Subject: [PATCH 2/3] feat(backend): add native HEIC support and fix EXIF orientation --- apps/web/backend/app/main.py | 55 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/web/backend/app/main.py b/apps/web/backend/app/main.py index 8878057..fcdbe9e 100644 --- a/apps/web/backend/app/main.py +++ b/apps/web/backend/app/main.py @@ -9,18 +9,19 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import Response, JSONResponse, FileResponse from fastapi.staticfiles import StaticFiles -from PIL import Image, ImageOps +from PIL import Image, ImageOps import pillow_heif # Required for HEIF/HEIC decoding import uvicorn -# Enable HEIF support globally for Pillow -pillow_heif.register_heif_opener() # Import withoutbg package (install via: uv sync or pip install -e ../../../packages/python) from withoutbg import WithoutBG, __version__ from withoutbg.exceptions import WithoutBGError from withoutbg.api import ProAPI +# Enable HEIF support globally for Pillow +pillow_heif.register_heif_opener() + app = FastAPI( title="withoutbg API", description="AI-powered background removal API", @@ -60,7 +61,7 @@ async def health_check(): "status": "healthy", "version": __version__, "service": "withoutbg-api", - "models_loaded": _model is not None + "models_loaded": _model is not None, } @@ -73,13 +74,13 @@ async def remove_background_endpoint( ): """ Remove background from a single image. - + Args: file: Image file to process format: Output format (png, jpg, webp) quality: Quality for JPEG output (1-100) api_key: Optional API key for cloud processing - + Returns: Processed image with background removed """ @@ -87,18 +88,20 @@ async def remove_background_endpoint( # Support standard image types and native Apple HEIC/HEIF is_image = file.content_type and file.content_type.startswith("image/") is_heic = file.filename and file.filename.lower().endswith((".heic", ".heif")) - + if not (is_image or is_heic): - raise HTTPException(status_code=400, detail="File must be an image (JPEG, PNG, or HEIC)") - + raise HTTPException( + status_code=400, detail="File must be an image (JPEG, PNG, or HEIC)" + ) + # Read uploaded file contents = await file.read() raw_image = Image.open(io.BytesIO(contents)) - + # 1. Apply EXIF orientation (prevents rotated mobile uploads) # 2. Force RGBA for consistency across inference models input_image = ImageOps.exif_transpose(raw_image).convert("RGBA") - + # Process image using appropriate model if api_key: # Use API for this specific request @@ -109,13 +112,13 @@ async def remove_background_endpoint( if _model is None: raise HTTPException( status_code=503, - detail="Models not loaded. Server may still be starting up." + detail="Models not loaded. Server may still be starting up.", ) result = _model.remove_background(input_image) - + # Convert result to bytes output_buffer = io.BytesIO() - + # Handle format conversion if format.lower() in ["jpg", "jpeg"]: # Convert RGBA to RGB for JPEG @@ -132,17 +135,15 @@ async def remove_background_endpoint( else: # PNG result.save(output_buffer, format="PNG") media_type = "image/png" - + output_buffer.seek(0) - + return Response( content=output_buffer.getvalue(), media_type=media_type, - headers={ - "Content-Disposition": f"inline; filename=withoutbg.{format}" - } + headers={"Content-Disposition": f"inline; filename=withoutbg.{format}"}, ) - + except WithoutBGError as e: raise HTTPException(status_code=500, detail=str(e)) except Exception as e: @@ -153,10 +154,10 @@ async def remove_background_endpoint( async def get_usage_endpoint(api_key: str): """ Get API usage statistics. - + Args: api_key: API key for cloud service - + Returns: Usage statistics """ @@ -172,7 +173,7 @@ async def get_usage_endpoint(api_key: str): if STATIC_DIR.exists(): # Serve static assets (js, css, images, etc.) app.mount("/assets", StaticFiles(directory=STATIC_DIR / "assets"), name="assets") - + # Root route - serve index.html @app.get("/") async def root(): @@ -181,7 +182,7 @@ async def root(): if index_path.exists(): return FileResponse(index_path) raise HTTPException(status_code=404, detail="Frontend not found") - + # Catch-all route for React SPA - must be last @app.get("/{full_path:path}") async def serve_frontend(full_path: str): @@ -189,17 +190,17 @@ async def serve_frontend(full_path: str): # Don't serve frontend for API routes if full_path.startswith("api/"): raise HTTPException(status_code=404, detail="API endpoint not found") - + # Try to serve the requested file file_path = STATIC_DIR / full_path if file_path.is_file(): return FileResponse(file_path) - + # Otherwise, serve index.html (SPA routing) index_path = STATIC_DIR / "index.html" if index_path.exists(): return FileResponse(index_path) - + raise HTTPException(status_code=404, detail="Not found") From d12a41c4f54abc82718e5e97497d9be88f06a874 Mon Sep 17 00:00:00 2001 From: kundan-krishna366 Date: Fri, 20 Mar 2026 15:11:19 +0530 Subject: [PATCH 3/3] feat(backend): add native HEIC support and fix EXIF orientation --- apps/web/backend/app/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/web/backend/app/main.py b/apps/web/backend/app/main.py index fcdbe9e..bfc789c 100644 --- a/apps/web/backend/app/main.py +++ b/apps/web/backend/app/main.py @@ -74,6 +74,9 @@ async def remove_background_endpoint( ): """ Remove background from a single image. + + Supports standard formats (PNG, JPG, WebP) and native Apple HEIC/HEIF files. + Automatically handles EXIF orientation to ensure upright output. Args: file: Image file to process