remove video specific stuff, base off of video, and add support for HTML
Some checks are pending
docker / Docker (push) Waiting to run
test / Test (push) Waiting to run

This commit is contained in:
Failure 2024-07-30 02:34:01 -07:00
parent 3d03f45eac
commit f348bb92e3
166 changed files with 85 additions and 1430 deletions

View file

@ -1,16 +1,14 @@
package imagorvideo
package imagorvideoextended
import (
"context"
"github.com/antchfx/htmlquery"
"github.com/cshum/imagor"
"github.com/cshum/imagor/imagorpath"
"github.com/cshum/imagorvideo/ffmpeg"
"github.com/gabriel-vasile/mimetype"
"go.uber.org/zap"
"io"
"strconv"
"strings"
"time"
)
// Processor for imagorvideo that implements imagor.Processor interface
@ -33,22 +31,6 @@ func NewProcessor(options ...Option) *Processor {
// Startup implements imagor.Processor interface
func (p *Processor) Startup(_ context.Context) error {
ffmpeg.SetLogging(func(level ffmpeg.AVLogLevel, message string) {
message = strings.TrimSuffix(message, "\n")
switch level {
case ffmpeg.AVLogTrace, ffmpeg.AVLogDebug, ffmpeg.AVLogVerbose:
p.Logger.Debug("ffmpeg", zap.String("log", message))
case ffmpeg.AVLogInfo:
p.Logger.Info("ffmpeg", zap.String("log", message))
case ffmpeg.AVLogWarning, ffmpeg.AVLogError, ffmpeg.AVLogFatal, ffmpeg.AVLogPanic:
p.Logger.Warn("ffmpeg", zap.String("log", message))
}
})
if p.Debug {
ffmpeg.SetFFmpegLogLevel(ffmpeg.AVLogDebug)
} else {
ffmpeg.SetFFmpegLogLevel(ffmpeg.AVLogError)
}
return nil
}
@ -75,15 +57,15 @@ func (p *Processor) Process(ctx context.Context, in *imagor.Blob, params imagorp
}
}
}()
var filters imagorpath.Filters
var mime = mimetype.Detect(in.Sniff())
if typ := mime.String(); !strings.HasPrefix(typ, "video/") &&
!strings.HasPrefix(typ, "audio/") {
if typ := mime.String(); !strings.HasPrefix(typ, "text/html") {
// forward identical for non video nor audio
err = imagor.ErrForward{Params: params}
out = in
return
}
rs, size, err := in.NewReadSeeker()
if err != nil {
return
@ -100,95 +82,61 @@ func (p *Processor) Process(ctx context.Context, in *imagor.Blob, params imagorp
return
}
}
av, err := ffmpeg.LoadAVContext(rs, size)
all, err := in.ReadAll()
if err != nil {
return
return nil, err
}
defer av.Close()
meta := av.Metadata()
if params.Meta {
out = imagor.NewBlobFromJsonMarshal(Metadata{
Format: strings.TrimPrefix(mime.Extension(), "."),
ContentType: mime.String(),
Metadata: meta,
})
return
doc, err := htmlquery.Parse(strings.NewReader(string(all[:])))
meta := Metadata{
Format: strings.TrimPrefix(mime.Extension(), "."),
Title: "",
Description: "",
Image: "",
}
bands := 3
for _, filter := range params.Filters {
switch filter.Name {
case "format":
switch strings.ToLower(filter.Args) {
case "webp", "png", "gif":
switch mime.Extension() {
case ".webm", ".flv", ".mov", ".avi":
bands = 4
}
}
case "frame":
if ts, e := time.ParseDuration(filter.Args); e == nil {
if err = av.SelectDuration(ts); err != nil {
return
}
} else if f, e := strconv.ParseFloat(filter.Args, 64); e == nil {
if strings.Contains(filter.Args, ".") {
if err = av.SelectPosition(f); err != nil {
return
}
} else if n := int(f); n >= 1 {
if err = av.SelectFrame(n); err != nil {
return
}
}
}
case "seek":
if ts, e := time.ParseDuration(filter.Args); e == nil {
if err = av.SeekDuration(ts); err != nil {
return
}
} else if f, e := strconv.ParseFloat(filter.Args, 64); e == nil {
if err = av.SeekPosition(f); err != nil {
return
}
}
case "max_frames":
n, _ := strconv.Atoi(filter.Args)
if err = av.ProcessFrames(n); err != nil {
return
}
metaTags := htmlquery.Find(doc, "//meta[@property]")
for _, metaTag := range metaTags {
var property = htmlquery.SelectAttr(metaTag, "property")
var val = htmlquery.SelectAttr(metaTag, "content")
switch property {
case "og:image":
fallthrough
case "twitter:image:src":
meta.Image = val
break
case "og:title":
fallthrough
case "twitter:title":
meta.Title = val
break
case "twitter:description":
fallthrough
case "og:description":
meta.Description = val
}
}
if meta.Title == "" {
title := htmlquery.FindOne(doc, "//title")
if title != nil {
meta.Title = htmlquery.InnerText(title)
} else {
meta.Title = in.FilePath()
}
}
switch meta.Orientation {
case 3:
filters = append(filters, imagorpath.Filter{Name: "orient", Args: "180"})
case 6:
filters = append(filters, imagorpath.Filter{Name: "orient", Args: "270"})
case 8:
filters = append(filters, imagorpath.Filter{Name: "orient", Args: "90"})
}
buf, err := av.Export(bands)
if err != nil || len(buf) == 0 {
if err == nil {
err = imagor.ErrUnsupportedFormat
}
return
}
out = imagor.NewBlobFromMemory(buf, meta.Width, meta.Height, bands)
if len(filters) > 0 {
params.Filters = append(params.Filters, filters...)
params.Path = imagorpath.GeneratePath(params)
}
err = imagor.ErrForward{Params: params}
out = imagor.NewBlobFromJsonMarshal(meta)
return
}
// Metadata imagorvideo metadata
type Metadata struct {
Format string `json:"format"`
ContentType string `json:"content_type"`
*ffmpeg.Metadata
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
}
var transPixel = []byte("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B")