refactor(thumb): new thumb pipeline model to generate thumb on-demand

This commit is contained in:
Aaron Liu
2023-04-07 19:08:54 +08:00
parent da1eaf2d1f
commit f36e39991d
23 changed files with 308 additions and 101 deletions

View File

@@ -1,7 +1,6 @@
package thumb
import (
"errors"
"fmt"
"image"
"image/gif"
@@ -18,6 +17,10 @@ import (
"golang.org/x/image/draw"
)
func init() {
RegisterGenerator(&Builtin{})
}
// Thumb 缩略图
type Thumb struct {
src image.Image
@@ -30,7 +33,7 @@ func NewThumbFromFile(file io.Reader, name string) (*Thumb, error) {
ext := strings.ToLower(filepath.Ext(name))
// 无扩展名时
if len(ext) == 0 {
return nil, errors.New("未知的图像类型")
return nil, fmt.Errorf("unknown image format: %w", ErrPassThrough)
}
var err error
@@ -45,7 +48,7 @@ func NewThumbFromFile(file io.Reader, name string) (*Thumb, error) {
case "png":
img, err = png.Decode(file)
default:
return nil, errors.New("unknown image format")
return nil, fmt.Errorf("unknown image format: %w", ErrPassThrough)
}
if err != nil {
return nil, err
@@ -70,18 +73,12 @@ func (image *Thumb) GetSize() (int, int) {
}
// Save 保存图像到给定路径
func (image *Thumb) Save(path string) (err error) {
out, err := util.CreatNestedFile(path)
if err != nil {
return err
}
defer out.Close()
func (image *Thumb) Save(w io.Writer) (err error) {
switch model.GetSettingByNameWithDefault("thumb_encode_method", "jpg") {
case "png":
err = png.Encode(out, image.src)
err = png.Encode(w, image.src)
default:
err = jpeg.Encode(out, image.src, &jpeg.Options{Quality: model.GetIntSetting("thumb_encode_quality", 85)})
err = jpeg.Encode(w, image.src, &jpeg.Options{Quality: model.GetIntSetting("thumb_encode_quality", 85)})
}
return err
@@ -141,9 +138,15 @@ func (image *Thumb) CreateAvatar(uid uint) error {
// 生成头像缩略图
src := image.src
for k, size := range []int{s, m, l} {
//image.src = resize.Resize(uint(size), uint(size), src, resize.Lanczos3)
out, err := util.CreatNestedFile(filepath.Join(savePath, fmt.Sprintf("avatar_%d_%d.png", uid, k)))
if err != nil {
return err
}
defer out.Close()
image.src = Resize(uint(size), uint(size), src)
err := image.Save(filepath.Join(savePath, fmt.Sprintf("avatar_%d_%d.png", uid, k)))
err = image.Save(out)
if err != nil {
return err
}
@@ -152,3 +155,23 @@ func (image *Thumb) CreateAvatar(uid uint) error {
return nil
}
type Builtin struct{}
func (b Builtin) Generate(file io.Reader, w io.Writer, name string, options map[string]string) error {
img, err := NewThumbFromFile(file, name)
if err != nil {
return err
}
img.GetThumb(thumbSize(options))
return img.Save(w)
}
func (b Builtin) Priority() int {
return 300
}
func (b Builtin) EnableFlag() string {
return "thumb_builtin_enabled"
}

88
pkg/thumb/pipeline.go Normal file
View File

@@ -0,0 +1,88 @@
package thumb
import (
"errors"
"fmt"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"io"
"sort"
"strconv"
)
// Generator generates a thumbnail for a given reader.
type Generator interface {
Generate(file io.Reader, w io.Writer, name string, options map[string]string) error
// Priority of execution order, smaller value means higher priority.
Priority() int
// EnableFlag returns the setting name to enable this generator.
EnableFlag() string
}
type (
GeneratorType string
GeneratorList []Generator
)
var (
Generators = GeneratorList{}
ErrPassThrough = errors.New("pass through")
ErrNotAvailable = fmt.Errorf("thumbnail not available: %w", ErrPassThrough)
)
func (g GeneratorList) Len() int {
return len(g)
}
func (g GeneratorList) Less(i, j int) bool {
return g[i].Priority() < g[j].Priority()
}
func (g GeneratorList) Swap(i, j int) {
g[i], g[j] = g[j], g[i]
}
// RegisterGenerator registers a thumbnail generator.
func RegisterGenerator(generator Generator) {
Generators = append(Generators, generator)
sort.Sort(Generators)
}
func (p GeneratorList) Generate(file io.Reader, w io.Writer, name string, options map[string]string) error {
for _, generator := range p {
if model.IsTrueVal(options[generator.EnableFlag()]) {
err := generator.Generate(file, w, name, options)
if errors.Is(err, ErrPassThrough) {
util.Log().Debug("Failed to generate thumbnail for %s: %s, passing through to next generator.", name, err)
continue
}
return err
}
}
return ErrNotAvailable
}
func (p GeneratorList) Priority() int {
return 0
}
func (p GeneratorList) EnableFlag() string {
return ""
}
func thumbSize(options map[string]string) (uint, uint) {
w, h := uint(400), uint(300)
if wParsed, err := strconv.Atoi(options["thumb_width"]); err == nil {
w = uint(wParsed)
}
if hParsed, err := strconv.Atoi(options["thumb_height"]); err == nil {
h = uint(hParsed)
}
return w, h
}