diff --git a/Dockerfile b/Dockerfile index 179a2c0..e135747 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,8 @@ FROM golang:alpine COPY --from=paperclip-server /dist /paperclip/server +RUN apk add --no-cache ffmpeg + EXPOSE 8445 CMD ["/paperclip/server"] diff --git a/go.mod b/go.mod index 0c2e829..71669be 100644 --- a/go.mod +++ b/go.mod @@ -91,6 +91,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/vansante/go-ffprobe.v2 v2.2.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/mysql v1.5.2 // indirect ) diff --git a/go.sum b/go.sum index 24cb8bb..e70313c 100644 --- a/go.sum +++ b/go.sum @@ -402,6 +402,8 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/vansante/go-ffprobe.v2 v2.2.0 h1:iuOqTsbfYuqIz4tAU9NWh22CmBGxlGHdgj4iqP+NUmY= +gopkg.in/vansante/go-ffprobe.v2 v2.2.0/go.mod h1:qF0AlAjk7Nqzqf3y333Ly+KxN3cKF2JqA3JT5ZheUGE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/internal/services/analyzer.go b/pkg/internal/services/analyzer.go index beec4c4..f3fcd01 100644 --- a/pkg/internal/services/analyzer.go +++ b/pkg/internal/services/analyzer.go @@ -1,6 +1,7 @@ package services import ( + "context" "crypto/sha256" "encoding/hex" "fmt" @@ -8,6 +9,7 @@ import ( "io" "os" "path/filepath" + "strconv" "strings" "time" @@ -18,6 +20,7 @@ import ( "github.com/rs/zerolog/log" "github.com/schollz/progressbar/v3" "github.com/spf13/viper" + "gopkg.in/vansante/go-ffprobe.v2" _ "image/gif" _ "image/jpeg" @@ -96,6 +99,7 @@ func AnalyzeAttachment(file models.Attachment) error { var start time.Time + // Do analyze job if !file.IsAnalyzed || len(file.HashCode) == 0 { destMap := viper.GetStringMap("destinations.temporary") @@ -110,7 +114,8 @@ func AnalyzeAttachment(file models.Attachment) error { return fmt.Errorf("attachment doesn't exists in temporary storage: %v", err) } - if t := strings.SplitN(file.MimeType, "/", 2)[0]; t == "image" { + switch strings.SplitN(file.MimeType, "/", 2)[0] { + case "image": // Dealing with image reader, err := os.Open(dst) if err != nil { @@ -129,6 +134,29 @@ func AnalyzeAttachment(file models.Attachment) error { "height": height, "ratio": ratio, } + case "video": + // Dealing with video + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + data, err := ffprobe.ProbeURL(ctx, dst) + if err != nil { + return fmt.Errorf("unable to analyze video information: %v", err) + } + + stream := data.FirstVideoStream() + ratio := float64(stream.Width) / float64(stream.Height) + duration, _ := strconv.ParseFloat(stream.Duration, 64) + file.Metadata = map[string]any{ + "width": stream.Width, + "height": stream.Height, + "ratio": ratio, + "duration": duration, + "bit_rate": stream.BitRate, + "codec_name": stream.CodecName, + "color_range": stream.ColorRange, + "color_space": stream.ColorSpace, + } } if hash, err := HashAttachment(file); err != nil {