github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/thumb/pipeline.go (about) 1 package thumb 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 model "github.com/cloudreve/Cloudreve/v3/models" 8 "github.com/cloudreve/Cloudreve/v3/pkg/util" 9 "io" 10 "os" 11 "path/filepath" 12 "reflect" 13 "sort" 14 "strconv" 15 ) 16 17 // Generator generates a thumbnail for a given reader. 18 type Generator interface { 19 // Generate generates a thumbnail for a given reader. Src is the original file path, only provided 20 // for local policy files. 21 Generate(ctx context.Context, file io.Reader, src string, name string, options map[string]string) (*Result, error) 22 23 // Priority of execution order, smaller value means higher priority. 24 Priority() int 25 26 // EnableFlag returns the setting name to enable this generator. 27 EnableFlag() string 28 } 29 30 type Result struct { 31 Path string 32 Continue bool 33 Cleanup []func() 34 } 35 36 type ( 37 GeneratorType string 38 GeneratorList []Generator 39 ) 40 41 var ( 42 Generators = GeneratorList{} 43 44 ErrPassThrough = errors.New("pass through") 45 ErrNotAvailable = fmt.Errorf("thumbnail not available: %w", ErrPassThrough) 46 ) 47 48 func (g GeneratorList) Len() int { 49 return len(g) 50 } 51 52 func (g GeneratorList) Less(i, j int) bool { 53 return g[i].Priority() < g[j].Priority() 54 } 55 56 func (g GeneratorList) Swap(i, j int) { 57 g[i], g[j] = g[j], g[i] 58 } 59 60 // RegisterGenerator registers a thumbnail generator. 61 func RegisterGenerator(generator Generator) { 62 Generators = append(Generators, generator) 63 sort.Sort(Generators) 64 } 65 66 func (p GeneratorList) Generate(ctx context.Context, file io.Reader, src, name string, options map[string]string) (*Result, error) { 67 inputFile, inputSrc, inputName := file, src, name 68 for _, generator := range p { 69 if model.IsTrueVal(options[generator.EnableFlag()]) { 70 res, err := generator.Generate(ctx, inputFile, inputSrc, inputName, options) 71 if errors.Is(err, ErrPassThrough) { 72 util.Log().Debug("Failed to generate thumbnail using %s for %s: %s, passing through to next generator.", reflect.TypeOf(generator).String(), name, err) 73 continue 74 } 75 76 if res != nil && res.Continue { 77 util.Log().Debug("Generator %s for %s returned continue, passing through to next generator.", reflect.TypeOf(generator).String(), name) 78 79 // defer cleanup funcs 80 for _, cleanup := range res.Cleanup { 81 defer cleanup() 82 } 83 84 // prepare file reader for next generator 85 intermediate, err := os.Open(res.Path) 86 if err != nil { 87 return nil, fmt.Errorf("failed to open intermediate thumb file: %w", err) 88 } 89 90 defer intermediate.Close() 91 inputFile = intermediate 92 inputSrc = res.Path 93 inputName = filepath.Base(res.Path) 94 continue 95 } 96 97 return res, err 98 } 99 } 100 return nil, ErrNotAvailable 101 } 102 103 func (p GeneratorList) Priority() int { 104 return 0 105 } 106 107 func (p GeneratorList) EnableFlag() string { 108 return "" 109 } 110 111 func thumbSize(options map[string]string) (uint, uint) { 112 w, h := uint(400), uint(300) 113 if wParsed, err := strconv.Atoi(options["thumb_width"]); err == nil { 114 w = uint(wParsed) 115 } 116 117 if hParsed, err := strconv.Atoi(options["thumb_height"]); err == nil { 118 h = uint(hParsed) 119 } 120 121 return w, h 122 }