Files
photoview/api/scanner/media_encoding/executable_worker/magick_cli.go
Googol Lee a378522af4 Use magick generating thumbnails.
Fix #1204

* Add bad medias to scanner_test.

* Fix the script.

* Add GenerateThumbnail function.

* Add identify dimension

* Use new API. Cover json condition.

* Use magick to generate thumbnails.

* Update go mod.

* Remove dependency of executable_worker -> test_utils

* Reduce dependency of magick.

* Use magick to get dimension.

* Remove almost all code around `image` package.

* Fix thumbnailing with multi-frame gif.

* Remove the struct in wrapper.

* Fix typo.

* Build images.

* Update logs

* Add jpeg decoder.

* Remove code dependency.

* Add log to confirm scan in parallel.

* Update log

* Add reformat image perf tests.

* Rollback the build workflow.
2025-05-22 09:01:43 +02:00

134 lines
2.8 KiB
Go

package executable_worker
import (
"bytes"
"encoding/json"
"fmt"
"os/exec"
"strings"
"github.com/photoview/photoview/api/log"
"github.com/photoview/photoview/api/utils"
)
type MagickCli struct {
path string
err error
}
func newMagickCli() *MagickCli {
if utils.EnvDisableRawProcessing.GetBool() {
log.Warn("Executable magick worker disabled", utils.EnvDisableRawProcessing.GetName(), utils.EnvDisableRawProcessing.GetValue())
return &MagickCli{
err: ErrDisabledFunction,
}
}
path, err := exec.LookPath("magick")
if err != nil {
log.Error("Executable magick worker not found")
return &MagickCli{
err: ErrNoDependency,
}
}
version, err := exec.Command(path, "-version").Output()
if err != nil {
log.Error("Executable magick worker get version error", "error", err)
return &MagickCli{
err: ErrNoDependency,
}
}
log.Info("Found magick executable worker", "version", strings.Split(string(version), "\n")[0])
return &MagickCli{
path: path,
}
}
func (cli *MagickCli) IsInstalled() bool {
return cli.err == nil
}
func (cli *MagickCli) EncodeJpeg(inputPath string, outputPath string, jpegQuality int) error {
if cli.err != nil {
return fmt.Errorf("encoding jpeg %q error: magick: %w", inputPath, cli.err)
}
args := []string{
inputPath,
"-auto-orient",
"-quality", fmt.Sprintf("%d", jpegQuality),
outputPath,
}
cmd := exec.Command(cli.path, args...)
if err := cmd.Run(); err != nil {
return fmt.Errorf("encoding image with \"%s %v\" error: %w", cli.path, args, err)
}
return nil
}
func (cli *MagickCli) GenerateThumbnail(inputPath string, outputPath string, width, height int) error {
if cli.err != nil {
return fmt.Errorf("generate thumbnail %q error: magick: %w", inputPath, cli.err)
}
args := []string{
inputPath + "[0]", // If there are multiple frames (like gif), only thumbnail the first frame.
"-thumbnail",
fmt.Sprintf("%dx%d", width, height),
outputPath,
}
cmd := exec.Command(cli.path, args...)
if err := cmd.Run(); err != nil {
return fmt.Errorf("generate thumbnail with \"%s %v\" error: %w", cli.path, args, err)
}
return nil
}
func (cli *MagickCli) IdentifyDimension(inputPath string) (width, height int, err error) {
if cli.err != nil {
err = fmt.Errorf("identify dimension %q error: magick: %w", inputPath, cli.err)
return
}
args := []string{
"identify",
"-format",
`{"height":%H, "width":%W}`,
inputPath,
}
cmd := exec.Command(cli.path, args...)
var output bytes.Buffer
cmd.Stdout = &output
if e := cmd.Run(); e != nil {
err = fmt.Errorf("identify dimension with \"%s %v\" error: %w", cli.path, args, e)
return
}
ret := struct {
Width *int
Height *int
}{
Width: &width,
Height: &height,
}
if e := json.NewDecoder(&output).Decode(&ret); e != nil {
err = fmt.Errorf("identify dimension with \"%s %v\" error: %w", cli.path, args, e)
return
}
return
}