func clip()

in clips/clips.go [34:144]


func clip(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

	///////////
	// Sanity check the input
	///////////

	//Check if request has "channel" parameter
	var channel string
	if val, ok := request.QueryStringParameters["channel"]; ok {
		channel = val
	} else {
		return events.APIGatewayProxyResponse{
			StatusCode: 400,
			Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
			Body:       encodeErrorResponse("Please provide a channel"),
		}, nil
	}

	//Does the URL match the pattern?
	if !channelPattern.MatchString(channel) {
		return events.APIGatewayProxyResponse{
			StatusCode: 400,
			Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
			Body:       encodeErrorResponse("Please provide an accepted playback url"),
		}, nil
	}

	//Extract the AWS Account ID and make sure it matches the lambda is currently running in.
	match := channelPattern.FindStringSubmatch(channel)
	if request.RequestContext.AccountID != match[3] {
		return events.APIGatewayProxyResponse{
			StatusCode: 400,
			Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
			Body:       encodeErrorResponse("Please provide an allowlisted playback url"),
		}, nil
	}

	///////////
	// Perform the segment download & stitch
	///////////

	// Download the segments for a given manifest URL.
	segments, err := hls.GetSegments(channel)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: 500,
			Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
			Body:       encodeErrorResponse("Could not clip."),
		}, nil
	}

	// Stitch together the segments using FFmpeg and write a new .mp4 into /tmp
	filename := uuid.New().String() + ".mp4"
	tempPath := path.Join("/tmp/" + filename)
	err = remux.Remux(segments, tempPath)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: 500,
			Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
			Body:       encodeErrorResponse("Could not clip."),
		}, nil
	}

	///////////
	// Upload the segement to S3
	///////////

	// Read generated .mp4 back into memory
	mp4, err := ioutil.ReadFile(tempPath)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: 500,
			Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
			Body:       encodeErrorResponse("Could not clip."),
		}, nil
	}

	// Upload file to S3 from memory
	sess := session.Must(session.NewSession())
	uploader := s3manager.NewUploader(sess)
	_, err = uploader.Upload(&s3manager.UploadInput{
		Bucket: aws.String(os.Getenv("bucket")),
		Key:    aws.String(path.Join("clips/", filename)),
		Body:   bytes.NewReader(mp4),
	})

	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: 500,
			Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
			Body:       encodeErrorResponse("Could not clip."),
		}, nil
	}

	os.Remove(tempPath)

	// Respond with URL to clip

	rs := Response{
		URL: "https://" + path.Join(os.Getenv("url"), "clips/", filename),
	}

	resp, _ := json.Marshal(rs)

	return events.APIGatewayProxyResponse{
		StatusCode: 200,
		Headers:    map[string]string{"Access-Control-Allow-Origin": "*"},
		Body:       string(resp),
	}, nil

}