@@ -908,211 +908,3 @@ async def redact_audio_download(
908908 raise HTTPException (
909909 status_code = 500 , detail = f"Audio redaction failed: { str (e )} "
910910 ) from e
911-
912-
913- @router .post ("/redact/audio" , status_code = status .HTTP_200_OK )
914- async def redact_audio_direct (
915- file : UploadFile = File (..., description = "Audio file to redact" ),
916- prompt : str = Form (..., description = "Natural language redaction instructions" ),
917- ):
918- """Redact/censor sensitive information from an audio file.
919-
920- This endpoint:
921- 1. Accepts an audio file upload and redaction prompt
922- 2. Uses Gemini to transcribe and identify content to censor
923- 3. Replaces identified segments with beep sounds
924- 4. Returns the censored audio as base64 encoded data
925-
926- Args:
927- file: Audio file to redact (MP3, WAV, OGG, M4A, FLAC, AAC, WebM)
928- prompt: Natural language description of what to censor
929-
930- Returns:
931- JSON with:
932- - censored_audio: base64 encoded censored audio
933- - segments_censored: number of segments censored
934- - total_censored_duration: total duration censored in seconds
935- - transcript: full transcript of the audio
936- - reasoning: explanation of censorship decisions
937- - targets: list of censored segments with timestamps
938- """
939- # Validate file type
940- if not file .filename :
941- raise HTTPException (status_code = 400 , detail = "Filename is required" )
942-
943- # Check file extension
944- file_ext = file .filename .lower ().split ("." )[- 1 ]
945- if file_ext not in {"mp3" , "wav" , "ogg" , "m4a" , "flac" , "aac" , "webm" }:
946- raise HTTPException (
947- status_code = 400 ,
948- detail = f"Unsupported file type: .{ file_ext } . Supported: mp3, wav, ogg, m4a, flac, aac, webm" ,
949- )
950-
951- # Validate content type if provided
952- if file .content_type and file .content_type not in SUPPORTED_AUDIO_TYPES :
953- logger .warning (f"Unexpected content type: { file .content_type } " )
954-
955- # Check for API key
956- api_key = os .environ .get ("GOOGLE_API_KEY" ) or os .environ .get ("GEMINI_API_KEY" )
957- if not api_key :
958- raise HTTPException (
959- status_code = 503 ,
960- detail = "Audio redaction service unavailable: GOOGLE_API_KEY or GEMINI_API_KEY not configured" ,
961- )
962-
963- try :
964- # Read audio content
965- content = await file .read ()
966-
967- logger .info (f"Processing audio redaction for: { file .filename } " )
968- logger .info (f"Prompt: { prompt } " )
969- logger .info (f"File size: { len (content ) / 1024 :.1f} KB" )
970-
971- # Import agent
972- from app .agents .audio_redaction import AudioRedactionAgent
973-
974- # Create agent and process
975- agent = AudioRedactionAgent (api_key = api_key )
976-
977- # Determine output format (keep same as input for common formats)
978- output_format = file_ext if file_ext in {"mp3" , "wav" , "ogg" , "flac" } else "mp3"
979-
980- # Run redaction
981- response = agent .redact_audio (
982- audio_data = content ,
983- prompt = prompt ,
984- file_ext = file_ext ,
985- output_format = output_format ,
986- )
987-
988- logger .info (
989- f"Audio redaction complete: { response .segments_censored } segments censored"
990- )
991-
992- return {
993- "censored_audio" : response .censored_audio ,
994- "segments_censored" : response .segments_censored ,
995- "segments_found" : response .segments_found ,
996- "total_censored_duration" : response .total_censored_duration ,
997- "audio_duration_seconds" : response .audio_duration_seconds ,
998- "processing_time_seconds" : response .processing_time_seconds ,
999- "transcript" : response .transcript ,
1000- "reasoning" : response .reasoning ,
1001- "targets" : [
1002- {
1003- "start_time" : t .start_time ,
1004- "end_time" : t .end_time ,
1005- "text" : t .text [:100 ] + "..." if len (t .text ) > 100 else t .text ,
1006- "reason" : t .reason ,
1007- }
1008- for t in response .targets
1009- ],
1010- "output_format" : output_format ,
1011- }
1012-
1013- except ImportError as e :
1014- logger .error (f"Missing dependency: { e } " )
1015- raise HTTPException (
1016- status_code = 503 ,
1017- detail = f"Audio redaction service unavailable: missing dependency ({ str (e )} ). Make sure pydub and ffmpeg are installed." ,
1018- ) from e
1019- except ValueError as e :
1020- logger .error (f"Audio redaction failed: { e } " )
1021- raise HTTPException (status_code = 400 , detail = str (e )) from e
1022- except Exception as e :
1023- logger .error (f"Audio redaction failed: { e } " , exc_info = True )
1024- raise HTTPException (
1025- status_code = 500 , detail = f"Audio redaction failed: { str (e )} "
1026- ) from e
1027-
1028-
1029- @router .post ("/redact/audio/download" , status_code = status .HTTP_200_OK )
1030- async def redact_audio_download (
1031- file : UploadFile = File (..., description = "Audio file to redact" ),
1032- prompt : str = Form (..., description = "Natural language redaction instructions" ),
1033- ):
1034- """Redact an audio file and return it as a downloadable file.
1035-
1036- Same as /redact/audio but returns the audio directly for download
1037- instead of base64 encoded JSON.
1038- """
1039- # Validate file type
1040- if not file .filename :
1041- raise HTTPException (status_code = 400 , detail = "Filename is required" )
1042-
1043- file_ext = file .filename .lower ().split ("." )[- 1 ]
1044- if file_ext not in {"mp3" , "wav" , "ogg" , "m4a" , "flac" , "aac" , "webm" }:
1045- raise HTTPException (
1046- status_code = 400 , detail = f"Unsupported file type: .{ file_ext } "
1047- )
1048-
1049- # Check for API key
1050- api_key = os .environ .get ("GOOGLE_API_KEY" ) or os .environ .get ("GEMINI_API_KEY" )
1051- if not api_key :
1052- raise HTTPException (
1053- status_code = 503 ,
1054- detail = "Audio redaction service unavailable: GOOGLE_API_KEY or GEMINI_API_KEY not configured" ,
1055- )
1056-
1057- try :
1058- # Read audio content
1059- content = await file .read ()
1060-
1061- # Import agent
1062- from app .agents .audio_redaction import AudioRedactionAgent
1063-
1064- # Create agent and process
1065- agent = AudioRedactionAgent (api_key = api_key )
1066-
1067- # Determine output format
1068- output_format = file_ext if file_ext in {"mp3" , "wav" , "ogg" , "flac" } else "mp3"
1069-
1070- # Run redaction
1071- response = agent .redact_audio (
1072- audio_data = content ,
1073- prompt = prompt ,
1074- file_ext = file_ext ,
1075- output_format = output_format ,
1076- )
1077-
1078- # Decode base64 audio
1079- censored_audio_bytes = base64 .b64decode (response .censored_audio )
1080-
1081- # Generate output filename
1082- original_name = file .filename
1083- name_parts = original_name .rsplit ("." , 1 )
1084- if len (name_parts ) == 2 :
1085- output_name = f"{ name_parts [0 ]} _censored.{ output_format } "
1086- else :
1087- output_name = f"{ original_name } _censored.{ output_format } "
1088-
1089- # Determine content type
1090- content_type_map = {
1091- "mp3" : "audio/mpeg" ,
1092- "wav" : "audio/wav" ,
1093- "ogg" : "audio/ogg" ,
1094- "flac" : "audio/flac" ,
1095- "m4a" : "audio/mp4" ,
1096- "aac" : "audio/aac" ,
1097- "webm" : "audio/webm" ,
1098- }
1099- content_type = content_type_map .get (output_format , "audio/mpeg" )
1100-
1101- return Response (
1102- content = censored_audio_bytes ,
1103- media_type = content_type ,
1104- headers = {"Content-Disposition" : f'attachment; filename="{ output_name } "' },
1105- )
1106-
1107- except ImportError as e :
1108- raise HTTPException (
1109- status_code = 503 ,
1110- detail = f"Audio redaction service unavailable: missing dependency ({ str (e )} )" ,
1111- ) from e
1112- except ValueError as e :
1113- raise HTTPException (status_code = 400 , detail = str (e )) from e
1114- except Exception as e :
1115- logger .error (f"Audio redaction failed: { e } " , exc_info = True )
1116- raise HTTPException (
1117- status_code = 500 , detail = f"Audio redaction failed: { str (e )} "
1118- ) from e
0 commit comments