Init V4 community edition (#2265)
* Init V4 community edition * Init V4 community edition
This commit is contained in:
@@ -4,93 +4,114 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager/entitysource"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Generator generates a thumbnail for a given reader.
|
||||
type Generator interface {
|
||||
// Generate generates a thumbnail for a given reader. Src is the original file path, only provided
|
||||
// for local policy files.
|
||||
Generate(ctx context.Context, file io.Reader, src string, name string, options map[string]string) (*Result, error)
|
||||
|
||||
// Priority of execution order, smaller value means higher priority.
|
||||
Priority() int
|
||||
|
||||
// EnableFlag returns the setting name to enable this generator.
|
||||
EnableFlag() string
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Path string
|
||||
Continue bool
|
||||
Cleanup []func()
|
||||
}
|
||||
|
||||
type (
|
||||
// Generator generates a thumbnail for a given reader.
|
||||
Generator interface {
|
||||
// Generate generates a thumbnail for a given reader. Src is the original file path, only provided
|
||||
// for local policy files. State is the result from previous generators, and can be read by current
|
||||
// generator for intermedia result.
|
||||
Generate(ctx context.Context, es entitysource.EntitySource, ext string, previous *Result) (*Result, error)
|
||||
|
||||
// Priority of execution order, smaller value means higher priority.
|
||||
Priority() int
|
||||
|
||||
// Enabled returns if current generator is enabled.
|
||||
Enabled(ctx context.Context) bool
|
||||
}
|
||||
Result struct {
|
||||
Path string
|
||||
Ext string
|
||||
Continue bool
|
||||
Cleanup []func()
|
||||
}
|
||||
GeneratorType string
|
||||
GeneratorList []Generator
|
||||
|
||||
generatorList []Generator
|
||||
pipeline struct {
|
||||
generators generatorList
|
||||
settings setting.Provider
|
||||
l logging.Logger
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
Generators = GeneratorList{}
|
||||
|
||||
ErrPassThrough = errors.New("pass through")
|
||||
ErrNotAvailable = fmt.Errorf("thumbnail not available: %w", ErrPassThrough)
|
||||
)
|
||||
|
||||
func (g GeneratorList) Len() int {
|
||||
func (g generatorList) Len() int {
|
||||
return len(g)
|
||||
}
|
||||
|
||||
func (g GeneratorList) Less(i, j int) bool {
|
||||
func (g generatorList) Less(i, j int) bool {
|
||||
return g[i].Priority() < g[j].Priority()
|
||||
}
|
||||
|
||||
func (g GeneratorList) Swap(i, j int) {
|
||||
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)
|
||||
// NewPipeline creates a new pipeline with all available generators.
|
||||
func NewPipeline(settings setting.Provider, l logging.Logger) Generator {
|
||||
generators := generatorList{}
|
||||
generators = append(
|
||||
generators,
|
||||
NewBuiltinGenerator(settings),
|
||||
NewFfmpegGenerator(l, settings),
|
||||
NewVipsGenerator(l, settings),
|
||||
NewLibreOfficeGenerator(l, settings),
|
||||
NewMusicCoverGenerator(l, settings),
|
||||
)
|
||||
sort.Sort(generators)
|
||||
|
||||
return pipeline{
|
||||
generators: generators,
|
||||
settings: settings,
|
||||
l: l,
|
||||
}
|
||||
}
|
||||
|
||||
func (p GeneratorList) Generate(ctx context.Context, file io.Reader, src, name string, options map[string]string) (*Result, error) {
|
||||
inputFile, inputSrc, inputName := file, src, name
|
||||
for _, generator := range p {
|
||||
if model.IsTrueVal(options[generator.EnableFlag()]) {
|
||||
res, err := generator.Generate(ctx, inputFile, inputSrc, inputName, options)
|
||||
func (p pipeline) Generate(ctx context.Context, es entitysource.EntitySource, ext string, state *Result) (*Result, error) {
|
||||
e := es.Entity()
|
||||
for _, generator := range p.generators {
|
||||
if generator.Enabled(ctx) {
|
||||
if _, err := es.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, fmt.Errorf("thumb: failed to seek to start of file: %w", err)
|
||||
}
|
||||
|
||||
res, err := generator.Generate(ctx, es, ext, state)
|
||||
if errors.Is(err, ErrPassThrough) {
|
||||
util.Log().Debug("Failed to generate thumbnail using %s for %s: %s, passing through to next generator.", reflect.TypeOf(generator).String(), name, err)
|
||||
p.l.Debug("Failed to generate thumbnail using %s for %s: %s, passing through to next generator.", reflect.TypeOf(generator).String(), e.Source(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if res != nil && res.Continue {
|
||||
util.Log().Debug("Generator %s for %s returned continue, passing through to next generator.", reflect.TypeOf(generator).String(), name)
|
||||
p.l.Debug("Generator %s for %s returned continue, passing through to next generator.", reflect.TypeOf(generator).String(), e.Source())
|
||||
|
||||
// defer cleanup funcs
|
||||
// defer cleanup functions
|
||||
for _, cleanup := range res.Cleanup {
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
// prepare file reader for next generator
|
||||
intermediate, err := os.Open(res.Path)
|
||||
state = res
|
||||
es, err = es.CloneToLocalSrc(types.EntityTypeVersion, res.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open intermediate thumb file: %w", err)
|
||||
return nil, fmt.Errorf("thumb: failed to clone to local source: %w", err)
|
||||
}
|
||||
|
||||
defer intermediate.Close()
|
||||
inputFile = intermediate
|
||||
inputSrc = res.Path
|
||||
inputName = filepath.Base(res.Path)
|
||||
defer es.Close()
|
||||
ext = util.Ext(res.Path)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -100,23 +121,10 @@ func (p GeneratorList) Generate(ctx context.Context, file io.Reader, src, name s
|
||||
return nil, ErrNotAvailable
|
||||
}
|
||||
|
||||
func (p GeneratorList) Priority() int {
|
||||
func (p pipeline) 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
|
||||
func (p pipeline) Enabled(ctx context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user