from pydantic import BaseModel from fastapi import FastAPI, UploadFile, HTTPException from ffmpeg import input, output, run from botocore.exceptions import ClientError import logging import ffmpeg import boto3 import json import os logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) app = FastAPI() # Define your AWS S3 credentials and bucket name aws_access_key_id = os.environ['AWS_ACCESS_KEY_ID'] aws_secret_access_key = os.environ['AWS_SECRET_ACCESS_KEY'] s3_bucket_name = os.environ['S3_BUCKET_NAME'] aws_env = os.environ['AWS_ENV'] aws_region = 'eu-central-1' temp_dir = '/tmp/' # Declare buffer for nicer cuts as float cutout_buffer = float(os.environ['HF_BUFFER_VALUE']) # Initialize an S3 client s3_client = boto3.client('s3', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, region_name=aws_region) class CutRequest(BaseModel): quote_filename: str segments: list[tuple[float, float]] news_name: str news_id: int class CutRequestInput(BaseModel): data: CutRequest def download_file(news_name: str, quote_filename: str, new_filename: str = "source.mp3"): # logger.debug(f"Downloading file: news_name={news_name}, quote_filename={quote_filename}") s3_directory = f'{aws_env}/{news_name}' s3_object_key = f'uploads/{s3_directory}/{quote_filename}' local_file = os.path.join(temp_dir, new_filename) try: # Ensure the download directory exists os.makedirs(temp_dir, exist_ok=True) # logger.debug(f"Downloading from S3: bucket={s3_bucket_name}, key={s3_object_key}, local_file={local_file}") s3_client.download_file(s3_bucket_name, s3_object_key, local_file) # logger.debug(f"File downloaded and saved as: {local_file}") # Return the local_file path return local_file except ClientError as e: if e.response['Error']['Code'] == "404": # logger.error(f"File not found in S3: {s3_object_key}") raise HTTPException(status_code=404, detail=f"The file {quote_filename} does not exist in S3.") else: # logger.error(f"S3 download error: {str(e)}") raise HTTPException(status_code=500, detail=f"Failed to download file from S3: {str(e)}") except Exception as e: # logger.exception(f"Unexpected error in download_file: {str(e)}") raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}") @app.post("/cut-audio") async def cut_audio(request: CutRequestInput): # logger.debug(f"Received request: {request}") try: cut_request = request.data # logger.debug(f"Parsed cut_request: {cut_request}") print(f"Received request: {cut_request}") download_file(cut_request.news_name, cut_request.quote_filename) for i, segment in enumerate(cut_request.segments): start, end = segment # logger.debug(f"Processing segment {i}: start={start}, end={end}") output_file = f"/tmp/cut_quote_{i}_{cut_request.quote_filename}" try: ( ffmpeg .input('/tmp/source.mp3', ss=start, to=end + cutout_buffer) .output(output_file) .run() ) except ffmpeg.Error as e: # logger.error(f"FFMPEG Error: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) try: s3_output_key = f'cut_outs/{aws_env}/{cut_request.news_id}/quote_segment_{i}.mp3' s3_client.upload_file(output_file, s3_bucket_name, s3_output_key) except Exception as e: # logger.error(f"S3 Upload Error: {str(e)}") raise HTTPException(status_code=500, detail=f"Failed to upload file to S3: {e}") return {"message": "Audio cut successfully"} except Exception as e: # logger.exception("Unexpected error in cut_audio") raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}") @app.post("/merge_audio/{output_key}") async def merge_audio(output_key: str): s3_directory = output_key # Output file name output_file = temp_dir + 'output.mp3' # Use python-ffmpeg to merge audio clips # input_file_paths = [temp_dir + input_file for input_file in input_files] # input_args = [input(file_path) for file_path in input_file_paths] input_file = 'list.txt' try: intermediate_output = temp_dir + 'intermediate_output.mp3' ffmpeg.input(input_file, format='concat', safe=0).output(intermediate_output, c='copy').run() ffmpeg.input(intermediate_output).audio.filter('loudnorm').output(output_file, audio_bitrate='256k', ar='44100').run() except Exception as e: raise HTTPException(status_code=500, detail=f"FFmpeg Error: {e}") # Upload the merged file back to S3 s3_output_key = f'{s3_directory}/final.mp3' try: s3_client.upload_file(output_file, s3_bucket_name, s3_output_key) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to upload file to S3: {e}") return {"message": "Audio clips successfully merged and saved in S3"} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=7680)