package thumb import ( "bytes" "context" "fmt" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/gofrs/uuid" "io" "os/exec" "path/filepath" "strings" ) func init() { RegisterGenerator(&VipsGenerator{}) } type VipsGenerator struct { exts []string lastRawExts string } func (v *VipsGenerator) Generate(ctx context.Context, file io.Reader, src, name string, options map[string]string) (*Result, error) { vipsOpts := model.GetSettingByNames("thumb_vips_path", "thumb_vips_exts", "thumb_encode_quality", "thumb_encode_method", "temp_path") if v.lastRawExts != vipsOpts["thumb_vips_exts"] { v.exts = strings.Split(vipsOpts["thumb_vips_exts"], ",") } if !util.IsInExtensionList(v.exts, name) { return nil, fmt.Errorf("unsupported image format: %w", ErrPassThrough) } outputOpt := ".png" if vipsOpts["thumb_encode_method"] == "jpg" { outputOpt = fmt.Sprintf(".jpg[Q=%s]", vipsOpts["thumb_encode_quality"]) } cmd := exec.CommandContext(ctx, vipsOpts["thumb_vips_path"], "thumbnail_source", "[descriptor=0]", outputOpt, options["thumb_width"], "--height", options["thumb_height"]) tempPath := filepath.Join( util.RelativePath(vipsOpts["temp_path"]), "thumb", fmt.Sprintf("thumb_%s", uuid.Must(uuid.NewV4()).String()), ) thumbFile, err := util.CreatNestedFile(tempPath) if err != nil { return nil, fmt.Errorf("failed to create temp file: %w", err) } defer thumbFile.Close() // Redirect IO var vipsErr bytes.Buffer cmd.Stdin = file cmd.Stdout = thumbFile cmd.Stderr = &vipsErr if err := cmd.Run(); err != nil { util.Log().Warning("Failed to invoke vips: %s", vipsErr.String()) return nil, fmt.Errorf("failed to invoke vips: %w", err) } return &Result{Path: tempPath}, nil } func (v *VipsGenerator) Priority() int { return 100 } func (v *VipsGenerator) EnableFlag() string { return "thumb_vips_enabled" }