import json import os import re import boto3 import requests MEDIACONVERT_DEST_BUCKET = "suno-media-dest" def get_s3_bucket_and_key(output_path): # Parse S3 URI to get bucket and key parts = output_path.replace("s3://", "").split("/", 1) bucket = parts[0] key = parts[1] return bucket, key def get_callback_auth_token(environment): secret_arn = os.environ[f"{environment.upper()}_SECRET_ARN"] secrets_client = boto3.client("secretsmanager") response = secrets_client.get_secret_value(SecretId=secret_arn) print(f"got response {response}") return response["SecretString"] def handler(event, context): print(f"event is {event}") callback_payload = { "status": "finished_media_convert", "request_id": event["id"], "id": event["id"], "type": "upload_video", "ok": 1, } # Handle Error Events if event["detail"]["status"] != "COMPLETE": print(f"MediaConvert job failed with status {event['detail']}") callback_payload["ok"] = 0 callback_payload["error"] = event["detail"]["errorMessage"] # Handle empty outputs output_group = event["detail"]["outputGroupDetails"] output_group_item_count = len(output_group) if not output_group_item_count: print( f"Unexpectedly got 0 items in MediaConvert output group, id {event['id']}" ) callback_payload["ok"] = 0 callback_payload["error"] = ( "Unexpectedly got 0 items in MediaConvert output group" ) clip_id = event["detail"]["userMetadata"].get("clip-id", None) environment = event["detail"]["userMetadata"].get("environment", "staging") callback_url = event["detail"]["userMetadata"].get("callback-url", None) skip_callback = ( event["detail"]["userMetadata"].get("skip-callback", "false").lower() == "true" ) print(f"got clip id {clip_id} and skip_callback {skip_callback}") request_id = None output_sizes = [] for output_details in output_group[0]["outputDetails"]: output_path = output_details["outputFilePaths"][0] match = re.search( r"/([^/]+)_(\d+)p\.m3u8$", output_path ) # e.g. 856479-uhd_4096_2160_25fps and 1080 from s3://suno-media-dest/85/64/856479-uhd_4096_2160_25fps_1080p.m3u8 if not match: print(f"Unexpected output path format: {output_path}") callback_payload["ok"] = 0 callback_payload["error"] = f"Unexpected output path format: {output_path}" break request_id = match.group(1) output_sizes.append(int(match.group(2))) if callback_payload["ok"]: callback_payload = { "status": "finished_media_convert", "request_id": request_id, "id": request_id, "type": "upload_video", "ok": 1, "video_cover_clip_id": clip_id, "video_streaming_sizes": output_sizes, } # Send callback to studio_api # callback_url = ( # f"https://studio-api.{environment}.suno.com/api/uploads/webhook/finish" # ) if skip_callback: print(f"Skipping callback for hook {request_id} because skip_callback is True") return if not callback_url: raise ValueError("callback_url is required, or specify skip_callback") callback_auth_token = get_callback_auth_token(environment) try: response = requests.post( callback_url, json=callback_payload, headers={ "Content-Type": "application/json", "Authentication": f"Bearer {callback_auth_token}", }, ) response.raise_for_status() print(f"Callback sent successfully: {response.status_code}") print(f"Callback payload: {json.dumps(callback_payload)}") except Exception as e: print(f"Error sending callback: {str(e)}") print(f"Callback payload was: {json.dumps(callback_payload)}") # Raise an exception here to fail the Lambda execution raise e return