FEAT: Media Convert + s3 Integration
This commit is contained in:
@@ -95,12 +95,19 @@ func CompleteVideoUpload(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
jobID, err := services.MediaConvertClient.QueueEncodingJob(r.Context(), key)
|
||||
if err != nil {
|
||||
renderUploadError(w, r, "Something Went Wrong On Creation Of Transcoding Job")
|
||||
return
|
||||
}
|
||||
|
||||
views.VideoUploadSuccess(views.VideoMetadata{
|
||||
Title: title,
|
||||
Description: strings.TrimSpace(req.Description),
|
||||
Category: strings.TrimSpace(req.Category),
|
||||
Tags: strings.TrimSpace(req.Tags),
|
||||
FileName: strings.TrimSpace(req.FileName),
|
||||
JobID: jobID,
|
||||
StoredAs: key,
|
||||
SizeBytes: 60,
|
||||
}).Render(r.Context(), w)
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/mediaconvert"
|
||||
"github.com/aws/aws-sdk-go-v2/service/mediaconvert/types"
|
||||
)
|
||||
|
||||
var MediaConvertClient MediaConvert
|
||||
|
||||
type MediaConvert interface {
|
||||
QueueEncodingJob(ctx context.Context, key string) (string, error)
|
||||
}
|
||||
|
||||
type MediaConvertConcrete struct {
|
||||
MediaConvertClient *mediaconvert.Client
|
||||
Role string
|
||||
InputBucket string
|
||||
OutputBucket string
|
||||
}
|
||||
|
||||
func (svc MediaConvertConcrete) QueueEncodingJob(ctx context.Context, key string) (string, error) {
|
||||
input := fmt.Sprintf("s3://%s/%s", svc.InputBucket, key)
|
||||
destination := fmt.Sprintf("s3://%s/%s", svc.OutputBucket, key)
|
||||
|
||||
job, err := svc.MediaConvertClient.CreateJob(ctx, &mediaconvert.CreateJobInput{
|
||||
Role: &svc.Role,
|
||||
Settings: &types.JobSettings{
|
||||
Inputs: []types.Input{
|
||||
{
|
||||
FileInput: &input,
|
||||
AudioSelectors: map[string]types.AudioSelector{
|
||||
"Audio Selector 1": {
|
||||
DefaultSelection: types.AudioDefaultSelectionDefault,
|
||||
},
|
||||
},
|
||||
VideoSelector: &types.VideoSelector{},
|
||||
},
|
||||
},
|
||||
OutputGroups: []types.OutputGroup{
|
||||
{
|
||||
Name: aws.String("File Group"),
|
||||
OutputGroupSettings: &types.OutputGroupSettings{
|
||||
Type: types.OutputGroupTypeFileGroupSettings,
|
||||
FileGroupSettings: &types.FileGroupSettings{
|
||||
Destination: &destination,
|
||||
},
|
||||
},
|
||||
Outputs: []types.Output{
|
||||
{
|
||||
ContainerSettings: &types.ContainerSettings{
|
||||
Container: types.ContainerTypeMp4,
|
||||
},
|
||||
VideoDescription: &types.VideoDescription{
|
||||
CodecSettings: &types.VideoCodecSettings{
|
||||
Codec: types.VideoCodecH264,
|
||||
H264Settings: &types.H264Settings{
|
||||
RateControlMode: types.H264RateControlModeQvbr,
|
||||
},
|
||||
},
|
||||
},
|
||||
AudioDescriptions: []types.AudioDescription{
|
||||
{
|
||||
CodecSettings: &types.AudioCodecSettings{
|
||||
Codec: types.AudioCodecAac,
|
||||
AacSettings: &types.AacSettings{
|
||||
Bitrate: aws.Int32(96000),
|
||||
CodingMode: types.AacCodingModeCodingMode20,
|
||||
SampleRate: aws.Int32(48000),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return aws.ToString(job.Job.Id), nil
|
||||
}
|
||||
@@ -2,8 +2,11 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
)
|
||||
|
||||
@@ -46,3 +49,38 @@ func (svc S3Concrete) DoesFileExist(ctx context.Context, key string) (bool, erro
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// AssertSuccessfulConnection verifies that the bucket is reachable and that
|
||||
// the configured credentials can get, put, and presign objects in it. It
|
||||
// panics with a descriptive message on the first check that fails, since the
|
||||
// service cannot function without these permissions.
|
||||
func (svc S3Concrete) AssertSuccessfulConnection(ctx context.Context) {
|
||||
if _, err := svc.S3Client.HeadBucket(ctx, &s3.HeadBucketInput{
|
||||
Bucket: &svc.Bucket,
|
||||
}); err != nil {
|
||||
panic(fmt.Errorf("s3: cannot connect to bucket %q: %w", svc.Bucket, err))
|
||||
}
|
||||
|
||||
const probeKey = ".s3-connectivity-check"
|
||||
|
||||
if _, err := svc.S3Client.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: &svc.Bucket,
|
||||
Key: aws.String(probeKey),
|
||||
Body: strings.NewReader("ok"),
|
||||
}); err != nil {
|
||||
panic(fmt.Errorf("s3: missing permission to put objects in bucket %q: %w", svc.Bucket, err))
|
||||
}
|
||||
|
||||
object, err := svc.S3Client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &svc.Bucket,
|
||||
Key: aws.String(probeKey),
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("s3: missing permission to get objects from bucket %q: %w", svc.Bucket, err))
|
||||
}
|
||||
object.Body.Close()
|
||||
|
||||
if _, err := svc.GetPresignedURL(ctx, probeKey, "text/plain", time.Minute); err != nil {
|
||||
panic(fmt.Errorf("s3: cannot create presigned urls for bucket %q: %w", svc.Bucket, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ type VideoMetadata struct {
|
||||
Tags string
|
||||
FileName string
|
||||
StoredAs string
|
||||
JobID string
|
||||
SizeBytes int64
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,8 @@ templ VideoUploadSuccess(v VideoMetadata) {
|
||||
}
|
||||
<dt>Category</dt>
|
||||
<dd>{ v.Category }</dd>
|
||||
<dt>Enqueue Job ID</dt>
|
||||
<dd>{ v.JobID }</dd>
|
||||
if v.Tags != "" {
|
||||
<dt>Tags</dt>
|
||||
<dd>{ v.Tags }</dd>
|
||||
|
||||
+13
-1
@@ -10,6 +10,7 @@ import (
|
||||
"thamanyah/cms/v2/internal/services"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/mediaconvert"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
)
|
||||
|
||||
@@ -19,11 +20,22 @@ func main() {
|
||||
panic(fmt.Errorf("failed To Load S3 Config: %s", err))
|
||||
}
|
||||
|
||||
services.S3Client = &services.S3Concrete{
|
||||
concreteS3Client := &services.S3Concrete{
|
||||
S3Client: s3.NewFromConfig(config),
|
||||
Bucket: "",
|
||||
}
|
||||
|
||||
concreteS3Client.AssertSuccessfulConnection(context.Background())
|
||||
|
||||
services.S3Client = concreteS3Client
|
||||
|
||||
services.MediaConvertClient = &services.MediaConvertConcrete{
|
||||
MediaConvertClient: mediaconvert.NewFromConfig(config),
|
||||
Role: "",
|
||||
InputBucket: "",
|
||||
OutputBucket: "",
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
|
||||
Reference in New Issue
Block a user