common/EndpointEncoding.go (140 lines of code) (raw):
package common
import (
"encoding/base64"
"fmt"
ddbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/aws/aws-sdk-go-v2/service/elastictranscoder/types"
"github.com/aws/smithy-go/rand"
"log"
"os"
"regexp"
"strconv"
"time"
)
/*
Encoding represents a row from the `Encodings` table
*/
type Encoding struct {
EncodingId int32 `json:"encoding_id"` //NOT NULL
ContentId int32 `json:"content_id"` //NOT NULL
Url string `json:"url"` //NOT NULL
Format string `json:"format"` //NOT NULL
Mobile bool `json:"mobile"` //NOT NULL
Multirate bool `json:"multirate"` //NOT NULL
VCodec string `json:"vcodec"`
ACodec string `json:"acodec"`
VBitrate int32 `json:"vbitrate"`
ABitrate int32 `json:"abitrate"`
LastUpdate time.Time `json:"last_update"` //NOT NULL, defaults to current time
FrameWidth int32 `json:"frame_width"` //NOT NULL
FrameHeight int32 `json:"frame_height"` //NOT NULL
Duration float32 `json:"duration"` //NOT NULL
FileSize int64 `json:"file_size"` //NOT NULL
FCSID string `json:"fcs_id"` //NOT NULL
OctopusId int32 `json:"octopus_id"` //NOT NULL aka 'title id'
Aspect string `json:"aspect"` //NOT NULL
}
func (e *Encoding) ToDynamoDB() map[string]ddbtypes.AttributeValue {
return map[string]ddbtypes.AttributeValue{
"encodingid": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.EncodingId)},
"contentid": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.ContentId)},
"url": &ddbtypes.AttributeValueMemberS{Value: e.Url},
"format": &ddbtypes.AttributeValueMemberS{Value: e.Format},
"mobile": &ddbtypes.AttributeValueMemberBOOL{Value: e.Mobile},
"multirate": &ddbtypes.AttributeValueMemberBOOL{Value: e.Multirate},
"vcodec": &ddbtypes.AttributeValueMemberS{Value: e.VCodec},
"acodec": &ddbtypes.AttributeValueMemberS{Value: e.ACodec},
"vbitrate": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.VBitrate)},
"abitrate": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.ABitrate)},
"lastupdate": &ddbtypes.AttributeValueMemberS{Value: e.LastUpdate.Format(time.RFC3339)},
"frame_width": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.FrameWidth)},
"frame_height": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.FrameHeight)},
"duration": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%f", e.Duration)},
"file_size": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.FileSize)},
"fcs_id": &ddbtypes.AttributeValueMemberS{Value: e.FCSID},
"octopus_id": &ddbtypes.AttributeValueMemberN{Value: fmt.Sprintf("%d", e.OctopusId)},
"aspect": &ddbtypes.AttributeValueMemberS{Value: e.Aspect},
}
}
/*
GenerateNumericId generates a (theoretically) unique numeric ID
*/
func GenerateNumericId() int32 {
randId, err := rand.CryptoRandInt63n(99999)
if err != nil {
log.Fatal("Could not generate random number")
}
return int32(randId) + 9900000
}
/*
GenerateStringIdPathSafe generates a randomised character string that is safe to use in a filepath
*/
func GenerateStringIdPathSafe() string {
baseId := GenerateStringId()
fixer := regexp.MustCompile("[^\\w\\d]")
return fixer.ReplaceAllString(baseId, "")
}
func GenerateStringId() string {
content, err := GenerateUuidBytes()
if err != nil {
log.Fatal("Could not get uuid bytes: ", err)
}
return base64.StdEncoding.EncodeToString(content)
}
func GenerateUuidBytes() ([]byte, error) {
entropy, err := os.Open("/dev/urandom")
if err != nil {
log.Fatal("Could not get entropy generator: ", err)
}
defer entropy.Close()
u := rand.NewUUID(entropy)
return u.GetBytes()
}
func GenerateUuidString() (string, error) {
entropy, err := os.Open("/dev/urandom")
if err != nil {
log.Fatal("Could not get entropy generator: ", err)
}
defer entropy.Close()
u := rand.NewUUID(entropy)
return u.GetUUID()
}
func JobOutputToEncoding(o *types.JobOutput, presetInfo *types.Preset, contentId int32, titleId int32, fcsId string, urlBase string) *Encoding {
encodingUrl := fmt.Sprintf("%s/%s", urlBase, *o.Key)
//log.Printf("Output file %s has format %s, vcodec %s, acodec %s, vbitrate %s, abitrate %s",
// *o.Key,
// *presetInfo.Container,
// *presetInfo.Video.Codec,
// *presetInfo.Audio.Codec,
// *presetInfo.Video.BitRate,
// *presetInfo.Audio.BitRate,
// )
formatMajor := "audio" //if the "video" part of the preset is configured we switch it to "video"
var bitRateNum int32
if videoPreset := presetInfo.Video; videoPreset != nil {
temp, err := strconv.ParseInt(*videoPreset.BitRate, 10, 32)
if err != nil {
log.Fatalf("Could not convert bitrate string '%s' to number: %s", *videoPreset.BitRate, err)
}
formatMajor = "video"
bitRateNum = int32(temp)
}
var audBitRateNum int32
if audioPreset := presetInfo.Audio; audioPreset != nil {
temp, err := strconv.ParseInt(*audioPreset.BitRate, 10, 32)
if err != nil {
log.Fatalf("Could not convert bitrate string '%s' to number: %s", *audioPreset.BitRate, err)
}
audBitRateNum = int32(temp)
}
var maybeAspectRatio string
if aspect := presetInfo.Video.AspectRatio; aspect != nil {
maybeAspectRatio = *aspect
} else {
maybeAspectRatio = ""
}
formatString := fmt.Sprintf("%s/%s", formatMajor, *presetInfo.Container)
return &Encoding{
EncodingId: GenerateNumericId(),
ContentId: contentId,
Url: encodingUrl,
Format: formatString,
Mobile: false,
Multirate: false,
VCodec: *presetInfo.Video.Codec,
ACodec: *presetInfo.Audio.Codec,
VBitrate: bitRateNum,
ABitrate: audBitRateNum,
LastUpdate: time.Now(),
FrameWidth: *o.Width,
FrameHeight: *o.Height,
Duration: float32(*o.Duration),
FileSize: *o.FileSize,
FCSID: fcsId,
OctopusId: titleId,
Aspect: maybeAspectRatio,
}
}