1111)
1212from typing import Dict , List , Optional , Any
1313from fastapi .responses import StreamingResponse
14+ import asyncio
1415import io
1516import re
1617import os
@@ -85,7 +86,8 @@ async def get_gallery_images(
8586 tag_list = [tag .strip () for tag in tags .split ("," ) if tag .strip ()]
8687
8788 # Query Cosmos DB for images only
88- result = cosmos_service .query_assets (
89+ result = await asyncio .to_thread (
90+ cosmos_service .query_assets ,
8991 media_type = "image" , # Images only
9092 folder_path = folder_path ,
9193 tags = tag_list ,
@@ -183,7 +185,8 @@ async def get_gallery_videos(
183185 tag_list = [tag .strip () for tag in tags .split ("," ) if tag .strip ()]
184186
185187 # Query Cosmos DB for videos only
186- result = cosmos_service .query_assets (
188+ result = await asyncio .to_thread (
189+ cosmos_service .query_assets ,
187190 media_type = "video" , # Videos only
188191 folder_path = folder_path ,
189192 tags = tag_list ,
@@ -310,7 +313,8 @@ async def _get_gallery_items_from_cosmos(
310313) -> GalleryResponse :
311314 """Get gallery items from CosmosDB metadata with standardized analysis structure"""
312315 try :
313- result = cosmos_service .query_assets (
316+ result = await asyncio .to_thread (
317+ cosmos_service .query_assets ,
314318 folder_path = folder_path ,
315319 limit = limit ,
316320 offset = offset ,
@@ -479,8 +483,9 @@ async def upload_asset(
479483
480484 cosmos_metadata = AssetMetadataCreateRequest (** cosmos_data )
481485
482- cosmos_service .create_asset_metadata (
483- cosmos_metadata .dict (exclude_unset = True )
486+ await asyncio .to_thread (
487+ cosmos_service .create_asset_metadata ,
488+ cosmos_metadata .dict (exclude_unset = True ),
484489 )
485490 except Exception as cosmos_error :
486491 # Log error but don't fail the upload
@@ -549,7 +554,9 @@ async def delete_asset(
549554 # Delete from Cosmos DB first (if available)
550555 if cosmos_service :
551556 try :
552- cosmos_service .delete_asset_metadata (asset_id , media_type_str )
557+ await asyncio .to_thread (
558+ cosmos_service .delete_asset_metadata , asset_id , media_type_str
559+ )
553560 except Exception as cosmos_error :
554561 import logging
555562
@@ -558,7 +565,9 @@ async def delete_asset(
558565 f"Failed to delete Cosmos DB metadata: { cosmos_error } " )
559566
560567 # Delete from Azure Blob Storage
561- success = azure_storage_service .delete_asset (blob_name , container_name )
568+ success = await asyncio .to_thread (
569+ azure_storage_service .delete_asset , blob_name , container_name
570+ )
562571
563572 if not success :
564573 raise HTTPException (status_code = 404 , detail = "Asset not found" )
@@ -598,8 +607,8 @@ async def get_asset_content(
598607 if media_type == MediaType .IMAGE
599608 else settings .AZURE_BLOB_VIDEO_CONTAINER
600609 )
601- content , content_type = azure_storage_service . get_asset_content (
602- blob_name , container_name
610+ content , content_type = await asyncio . to_thread (
611+ azure_storage_service . get_asset_content , blob_name , container_name
603612 )
604613
605614 if not content :
@@ -675,7 +684,9 @@ async def health_check(
675684 settings .AZURE_BLOB_VIDEO_CONTAINER ,
676685 ]
677686 for container in containers :
678- azure_storage_service ._ensure_container_exists (container )
687+ await asyncio .to_thread (
688+ azure_storage_service ._ensure_container_exists , container
689+ )
679690
680691 health_status ["services" ]["azure_blob_storage" ] = {
681692 "status" : "healthy" ,
@@ -692,7 +703,7 @@ async def health_check(
692703 # Check Cosmos DB
693704 if cosmos_service :
694705 try :
695- cosmos_health = cosmos_service .health_check ( )
706+ cosmos_health = await asyncio . to_thread ( cosmos_service .health_check )
696707 health_status ["services" ]["cosmos_db" ] = cosmos_health
697708
698709 if cosmos_health ["status" ] != "healthy" :
@@ -757,14 +768,18 @@ async def metadata_service_status(
757768 if cosmos_service :
758769 try :
759770 # Test basic connectivity
760- cosmos_health = cosmos_service .health_check ( )
771+ cosmos_health = await asyncio . to_thread ( cosmos_service .health_check )
761772
762773 # Test query capabilities
763- test_query_result = cosmos_service .query_assets (limit = 1 , offset = 0 )
774+ test_query_result = await asyncio .to_thread (
775+ cosmos_service .query_assets , limit = 1 , offset = 0
776+ )
764777
765778 # Test search capabilities
766779 try :
767- search_result = cosmos_service .search_assets ("test" , limit = 1 )
780+ search_result = await asyncio .to_thread (
781+ cosmos_service .search_assets , "test" , limit = 1
782+ )
768783 search_available = True
769784 except :
770785 search_available = False
@@ -847,11 +862,14 @@ async def list_folders(
847862
848863 # Get folders from Cosmos DB
849864 media_type_str = media_type .value if media_type else None
850- result = cosmos_service .get_all_folders (media_type = media_type_str )
865+ result = await asyncio .to_thread (
866+ cosmos_service .get_all_folders , media_type = media_type_str
867+ )
851868
852869 # Filter out root folder from the results and build hierarchy for UI
853- filtered_folders = [folder for folder in result ['folders' ] if folder != '/' and folder .strip ()]
854-
870+ filtered_folders = [
871+ folder for folder in result ['folders' ] if folder != '/' and folder .strip ()]
872+
855873 # Build folder hierarchy for UI
856874 folder_hierarchy = {}
857875 for folder_path in filtered_folders :
@@ -883,7 +901,7 @@ async def create_folder(
883901):
884902 """
885903 Create a folder placeholder in CosmosDB for immediate navbar visibility.
886-
904+
887905 This creates a special folder metadata record so the folder appears immediately
888906 in the navigation, even before any assets are added to it.
889907 """
@@ -918,13 +936,19 @@ async def create_folder(
918936 if cosmos_service :
919937 try :
920938 # Check if folder placeholder already exists
921- existing_placeholder = cosmos_service .container .query_items (
922- query = "SELECT * FROM c WHERE c.doc_type = 'folder_placeholder' AND c.folder_path = @folder_path" ,
923- parameters = [{"name" : "@folder_path" , "value" : normalized_folder }],
924- enable_cross_partition_query = True
939+ existing_placeholder = await asyncio .to_thread (
940+ lambda : list (
941+ cosmos_service .container .query_items (
942+ query = "SELECT * FROM c WHERE c.doc_type = 'folder_placeholder' AND c.folder_path = @folder_path" ,
943+ parameters = [
944+ {"name" : "@folder_path" , "value" : normalized_folder }
945+ ],
946+ enable_cross_partition_query = True ,
947+ )
948+ )
925949 )
926-
927- if not list ( existing_placeholder ) :
950+
951+ if not existing_placeholder :
928952 # Create folder placeholder record
929953 folder_placeholder = {
930954 "id" : f"folder_{ uuid .uuid4 ().hex [:12 ]} " ,
@@ -937,14 +961,19 @@ async def create_folder(
937961 "is_placeholder" : True ,
938962 "asset_count" : 0
939963 }
940-
941- cosmos_service .create_asset_metadata (folder_placeholder )
942- logger .info (f"Created folder placeholder for: { folder_path } " )
964+
965+ await asyncio .to_thread (
966+ cosmos_service .create_asset_metadata , folder_placeholder
967+ )
968+ logger .info (
969+ f"Created folder placeholder for: { folder_path } " )
943970 else :
944- logger .info (f"Folder placeholder already exists for: { folder_path } " )
945-
971+ logger .info (
972+ f"Folder placeholder already exists for: { folder_path } " )
973+
946974 except Exception as e :
947- logger .warning (f"Failed to create folder placeholder in CosmosDB: { e } " )
975+ logger .warning (
976+ f"Failed to create folder placeholder in CosmosDB: { e } " )
948977 # Continue anyway - folder will still work when assets are added
949978
950979 # Return success
0 commit comments