github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/thumb/libreoffice.go (about) 1 package thumb 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 model "github.com/cloudreve/Cloudreve/v3/models" 8 "github.com/cloudreve/Cloudreve/v3/pkg/util" 9 "github.com/gofrs/uuid" 10 "io" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "strings" 15 ) 16 17 func init() { 18 RegisterGenerator(&LibreOfficeGenerator{}) 19 } 20 21 type LibreOfficeGenerator struct { 22 exts []string 23 lastRawExts string 24 } 25 26 func (l *LibreOfficeGenerator) Generate(ctx context.Context, file io.Reader, src string, name string, options map[string]string) (*Result, error) { 27 sofficeOpts := model.GetSettingByNames("thumb_libreoffice_path", "thumb_libreoffice_exts", "thumb_encode_method", "temp_path") 28 29 if l.lastRawExts != sofficeOpts["thumb_libreoffice_exts"] { 30 l.exts = strings.Split(sofficeOpts["thumb_libreoffice_exts"], ",") 31 } 32 33 if !util.IsInExtensionList(l.exts, name) { 34 return nil, fmt.Errorf("unsupported document format: %w", ErrPassThrough) 35 } 36 37 tempOutputPath := filepath.Join( 38 util.RelativePath(sofficeOpts["temp_path"]), 39 "thumb", 40 fmt.Sprintf("soffice_%s", uuid.Must(uuid.NewV4()).String()), 41 ) 42 43 tempInputPath := src 44 if tempInputPath == "" { 45 // If not local policy files, download to temp folder 46 tempInputPath = filepath.Join( 47 util.RelativePath(sofficeOpts["temp_path"]), 48 "thumb", 49 fmt.Sprintf("soffice_%s%s", uuid.Must(uuid.NewV4()).String(), filepath.Ext(name)), 50 ) 51 52 // Due to limitations of ffmpeg, we need to write the input file to disk first 53 tempInputFile, err := util.CreatNestedFile(tempInputPath) 54 if err != nil { 55 return nil, fmt.Errorf("failed to create temp file: %w", err) 56 } 57 58 defer os.Remove(tempInputPath) 59 defer tempInputFile.Close() 60 61 if _, err = io.Copy(tempInputFile, file); err != nil { 62 return nil, fmt.Errorf("failed to write input file: %w", err) 63 } 64 65 tempInputFile.Close() 66 } 67 68 // Convert the document to an image 69 cmd := exec.CommandContext(ctx, sofficeOpts["thumb_libreoffice_path"], "--headless", 70 "-nologo", "--nofirststartwizard", "--invisible", "--norestore", "--convert-to", 71 sofficeOpts["thumb_encode_method"], "--outdir", tempOutputPath, tempInputPath) 72 73 // Redirect IO 74 var stdErr bytes.Buffer 75 cmd.Stdin = file 76 cmd.Stderr = &stdErr 77 78 if err := cmd.Run(); err != nil { 79 util.Log().Warning("Failed to invoke LibreOffice: %s", stdErr.String()) 80 return nil, fmt.Errorf("failed to invoke LibreOffice: %w", err) 81 } 82 83 return &Result{ 84 Path: filepath.Join( 85 tempOutputPath, 86 strings.TrimSuffix(filepath.Base(tempInputPath), filepath.Ext(tempInputPath))+"."+sofficeOpts["thumb_encode_method"], 87 ), 88 Continue: true, 89 Cleanup: []func(){func() { _ = os.RemoveAll(tempOutputPath) }}, 90 }, nil 91 } 92 93 func (l *LibreOfficeGenerator) Priority() int { 94 return 50 95 } 96 97 func (l *LibreOfficeGenerator) EnableFlag() string { 98 return "thumb_libreoffice_enabled" 99 }