Add log package for better logging level. (#1130)

* Add log package. Update worker with new log package.

* Add init error to worker.

* Init workers when binary launching.

* Add tests to cover different errors.
This commit is contained in:
Googol Lee
2024-11-21 10:44:38 +01:00
committed by GitHub
parent 0d69d5584e
commit a9d7b774bd
12 changed files with 289 additions and 110 deletions

View File

@@ -1,20 +1,24 @@
package executable_worker
import (
"errors"
"fmt"
"log"
"os/exec"
"strings"
"github.com/photoview/photoview/api/log"
"gopkg.in/vansante/go-ffprobe.v2"
)
func InitializeExecutableWorkers() {
var ErrNoDependency = errors.New("dependency not found")
var ErrDisabledFunction = errors.New("function disabled")
func init() {
Magick = newMagickCli()
Ffmpeg = newFfmpegCli()
if err := SetFfprobePath(); err != nil {
log.Println("ffprobe init fail:", err)
log.Error("Init ffprobe fail.", "error", err)
}
}
@@ -36,7 +40,7 @@ func SetFfprobePath() error {
return fmt.Errorf("Executable ffprobe(%q) not executable: %w", path, err)
}
log.Println("Found ffprobe:", path, "version:", strings.Split(string(version), "\n")[0])
log.Info("Found ffprobe", "path", path, "version", strings.Split(string(version), "\n")[0])
ffprobe.SetFFProbeBinPath(path)
return nil

View File

@@ -1,45 +1,39 @@
package executable_worker_test
package executable_worker
import (
"os"
"testing"
"github.com/photoview/photoview/api/scanner/media_encoding/executable_worker"
"github.com/photoview/photoview/api/test_utils"
"github.com/photoview/photoview/api/test_utils/test_env"
)
func TestMain(m *testing.M) {
os.Exit(test_utils.IntegrationTestRun(m))
}
const testdataBinPath = "./testdata/bin"
func TestInitFfprobePath(t *testing.T) {
t.Run("PathFail", func(t *testing.T) {
err := executable_worker.SetFfprobePath()
err := SetFfprobePath()
if err == nil {
t.Fatalf("InitFfprobePath() returns nil, want an error")
}
})
t.Run("VersionFail", func(t *testing.T) {
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
doneEnv := test_utils.SetEnv("FAIL_WITH", "expect failure")
doneEnv := test_env.SetEnv("FAIL_WITH", "expect failure")
defer doneEnv()
err := executable_worker.SetFfprobePath()
err := SetFfprobePath()
if err == nil {
t.Fatalf("InitFfprobePath() returns nil, want an error")
}
})
t.Run("Succeed", func(t *testing.T) {
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
err := executable_worker.SetFfprobePath()
err := SetFfprobePath()
if err != nil {
t.Fatalf("InitFfprobePath() returns %v, want nil", err)
}

View File

@@ -2,10 +2,10 @@ package executable_worker
import (
"fmt"
"log"
"os/exec"
"strings"
"github.com/photoview/photoview/api/log"
"github.com/photoview/photoview/api/utils"
"gopkg.in/vansante/go-ffprobe.v2"
)
@@ -21,24 +21,31 @@ var hwAccToCodec = map[string]string{
type FfmpegCli struct {
path string
videoCodec string
err error
}
func newFfmpegCli() *FfmpegCli {
if utils.EnvDisableVideoEncoding.GetBool() {
log.Printf("Executable worker disabled (%s=%q): ffmpeg\n", utils.EnvDisableVideoEncoding.GetName(), utils.EnvDisableVideoEncoding.GetValue())
return nil
log.Warn("Executable ffmpeg worker disabled", utils.EnvDisableVideoEncoding.GetName(), utils.EnvDisableVideoEncoding.GetValue())
return &FfmpegCli{
err: ErrDisabledFunction,
}
}
path, err := exec.LookPath("ffmpeg")
if err != nil {
log.Println("Executable worker not found: ffmpeg")
return nil
log.Error("Executable ffmpeg worker not found")
return &FfmpegCli{
err: ErrNoDependency,
}
}
version, err := exec.Command(path, "-version").Output()
if err != nil {
log.Printf("Error getting version of ffmpeg: %s\n", err)
return nil
log.Error("Executable ffmpeg worker getting version error", "error", err)
return &FfmpegCli{
err: ErrNoDependency,
}
}
hwAcc := utils.EnvVideoHardwareAcceleration.GetValue()
@@ -52,7 +59,7 @@ func newFfmpegCli() *FfmpegCli {
}
}
log.Printf("Found executable worker: ffmpeg (%s) with codec %q\n", strings.Split(string(version), "\n")[0], codec)
log.Info("Found executable worker: ffmpeg", "version", strings.Split(string(version), "\n")[0], "codec", codec)
return &FfmpegCli{
path: path,
@@ -60,31 +67,38 @@ func newFfmpegCli() *FfmpegCli {
}
}
func (worker *FfmpegCli) IsInstalled() bool {
return worker != nil
func (cli *FfmpegCli) IsInstalled() bool {
return cli.err == nil
}
func (worker *FfmpegCli) EncodeMp4(inputPath string, outputPath string) error {
func (cli *FfmpegCli) EncodeMp4(inputPath string, outputPath string) error {
if cli.err != nil {
return fmt.Errorf("encoding video %q error: ffmpeg: %w", inputPath, cli.err)
}
args := []string{
"-i",
inputPath,
"-vcodec", worker.videoCodec,
"-vcodec", cli.videoCodec,
"-acodec", "aac",
"-vf", "scale='min(1080,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2",
"-movflags", "+faststart+use_metadata_tags",
outputPath,
}
cmd := exec.Command(worker.path, args...)
cmd := exec.Command(cli.path, args...)
if err := cmd.Run(); err != nil {
return fmt.Errorf("encoding video with %q %v error: %w", worker.path, args, err)
return fmt.Errorf("encoding video with %q %v error: %w", cli.path, args, err)
}
return nil
}
func (worker *FfmpegCli) EncodeVideoThumbnail(inputPath string, outputPath string, probeData *ffprobe.ProbeData) error {
func (cli *FfmpegCli) EncodeVideoThumbnail(inputPath string, outputPath string, probeData *ffprobe.ProbeData) error {
if cli.err != nil {
return fmt.Errorf("encoding video thumbnail %q error: ffmpeg: %w", inputPath, cli.err)
}
thumbnailOffsetSeconds := fmt.Sprintf("%.f", probeData.Format.DurationSeconds*0.25)
@@ -98,10 +112,10 @@ func (worker *FfmpegCli) EncodeVideoThumbnail(inputPath string, outputPath strin
outputPath,
}
cmd := exec.Command(worker.path, args...)
cmd := exec.Command(cli.path, args...)
if err := cmd.Run(); err != nil {
return fmt.Errorf("encoding video thumbnail with %q %v error: %w", worker.path, args, err)
return fmt.Errorf("encoding video thumbnail with %q %v error: %w", cli.path, args, err)
}
return nil

View File

@@ -1,55 +1,105 @@
package executable_worker_test
package executable_worker
import (
"errors"
"regexp"
"testing"
"github.com/photoview/photoview/api/scanner/media_encoding/executable_worker"
"github.com/photoview/photoview/api/test_utils"
"github.com/photoview/photoview/api/test_utils/test_env"
"github.com/photoview/photoview/api/utils"
"gopkg.in/vansante/go-ffprobe.v2"
)
func TestFfmpegNotExist(t *testing.T) {
done := test_utils.SetPathWithCurrent()
done := test_env.SetPathWithCurrent()
defer done()
executable_worker.InitializeExecutableWorkers()
Ffmpeg = newFfmpegCli()
if executable_worker.Ffmpeg.IsInstalled() {
t.Error("Ffmpeg should not be installed, but is found:", executable_worker.Ffmpeg)
if got, want := Ffmpeg.err, ErrNoDependency; got != want {
t.Errorf("Ffmpeg.err = %v, want: %v", got, want)
}
if Ffmpeg.IsInstalled() {
t.Error("Ffmpeg should not be installed, but is found:", Ffmpeg)
}
if got, want := Ffmpeg.EncodeMp4("input", "output"), ErrNoDependency; !errors.Is(got, want) {
t.Errorf("Ffmpge.EncodeMp4() = %v, want: %v", got, want)
}
if got, want := Ffmpeg.EncodeVideoThumbnail("input", "output", nil), ErrNoDependency; !errors.Is(got, want) {
t.Errorf("Ffmpge.EncodeMp4() = %v, want: %v", got, want)
}
}
func TestFfmpegVersionFail(t *testing.T) {
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
doneEnv := test_env.SetEnv("FAIL_WITH", "expect failure")
defer doneEnv()
Ffmpeg = newFfmpegCli()
if got, want := Ffmpeg.err, ErrNoDependency; got != want {
t.Errorf("Ffmpeg.err = %v, want: %v", got, want)
}
if Ffmpeg.IsInstalled() {
t.Error("Ffmpeg should not be installed, but is found:", Ffmpeg)
}
if got, want := Ffmpeg.EncodeMp4("input", "output"), ErrNoDependency; !errors.Is(got, want) {
t.Errorf("Ffmpge.EncodeMp4() = %v, want: %v", got, want)
}
if got, want := Ffmpeg.EncodeVideoThumbnail("input", "output", nil), ErrNoDependency; !errors.Is(got, want) {
t.Errorf("Ffmpge.EncodeMp4() = %v, want: %v", got, want)
}
}
func TestFfmpegIgnore(t *testing.T) {
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
doneEnv := test_utils.SetEnv("PHOTOVIEW_DISABLE_VIDEO_ENCODING", "true")
doneEnv := test_env.SetEnv("PHOTOVIEW_DISABLE_VIDEO_ENCODING", "true")
defer doneEnv()
executable_worker.InitializeExecutableWorkers()
Ffmpeg = newFfmpegCli()
if executable_worker.Ffmpeg.IsInstalled() {
t.Error("Ffmpeg should be ignored (as it is disabled), but is initialized:", executable_worker.Ffmpeg)
if got, want := Ffmpeg.err, ErrDisabledFunction; got != want {
t.Errorf("Ffmpeg.err = %v, want: %v", got, want)
}
if Ffmpeg.IsInstalled() {
t.Error("Ffmpeg should be ignored (as it is disabled), but is initialized:", Ffmpeg)
}
if got, want := Ffmpeg.EncodeMp4("input", "output"), ErrDisabledFunction; !errors.Is(got, want) {
t.Errorf("Ffmpge.EncodeMp4() = %v, want: %v", got, want)
}
if got, want := Ffmpeg.EncodeVideoThumbnail("input", "output", nil), ErrDisabledFunction; !errors.Is(got, want) {
t.Errorf("Ffmpge.EncodeMp4() = %v, want: %v", got, want)
}
}
func TestFfmpeg(t *testing.T) {
done := test_utils.SetPathWithCurrent(testdataBinPath)
done := test_env.SetPathWithCurrent(testdataBinPath)
defer done()
executable_worker.InitializeExecutableWorkers()
Ffmpeg = newFfmpegCli()
if !executable_worker.Ffmpeg.IsInstalled() {
if !Ffmpeg.IsInstalled() {
t.Fatal("Ffmpeg should be installed")
}
t.Run("EncodeMp4Failed", func(t *testing.T) {
doneEnv := test_utils.SetEnv("FAIL_WITH", "expect failure")
doneEnv := test_env.SetEnv("FAIL_WITH", "expect failure")
defer doneEnv()
err := executable_worker.Ffmpeg.EncodeMp4("input", "output")
err := Ffmpeg.EncodeMp4("input", "output")
if err == nil {
t.Fatalf("Ffmpeg.EncodeMp4(...) = nil, should be an error.")
}
@@ -59,7 +109,7 @@ func TestFfmpeg(t *testing.T) {
})
t.Run("EncodeMp4Succeeded", func(t *testing.T) {
err := executable_worker.Ffmpeg.EncodeMp4("input", "output")
err := Ffmpeg.EncodeMp4("input", "output")
if err != nil {
t.Fatalf("Ffmpeg.EncodeMp4(...) = %v, should be nil.", err)
}
@@ -71,10 +121,10 @@ func TestFfmpeg(t *testing.T) {
},
}
t.Run("EncodeVideoThumbnailMp4Failed", func(t *testing.T) {
doneEnv := test_utils.SetEnv("FAIL_WITH", "expect failure")
doneEnv := test_env.SetEnv("FAIL_WITH", "expect failure")
defer doneEnv()
err := executable_worker.Ffmpeg.EncodeVideoThumbnail("input", "output", probeData)
err := Ffmpeg.EncodeVideoThumbnail("input", "output", probeData)
if err == nil {
t.Fatalf("Ffmpeg.EncodeVideoThumbnail(...) = nil, should be an error.")
}
@@ -84,7 +134,7 @@ func TestFfmpeg(t *testing.T) {
})
t.Run("EncodeVideoThumbnailSucceeded", func(t *testing.T) {
err := executable_worker.Ffmpeg.EncodeVideoThumbnail("input", "output", probeData)
err := Ffmpeg.EncodeVideoThumbnail("input", "output", probeData)
if err != nil {
t.Fatalf("Ffmpeg.EncodeVideoThumbnail(...) = %v, should be nil.", err)
}
@@ -92,18 +142,18 @@ func TestFfmpeg(t *testing.T) {
}
func TestFfmpegWithHWAcc(t *testing.T) {
doneCodec := test_utils.SetEnv(utils.EnvVideoHardwareAcceleration.GetName(), "qsv")
doneCodec := test_env.SetEnv(utils.EnvVideoHardwareAcceleration.GetName(), "qsv")
defer doneCodec()
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
executable_worker.InitializeExecutableWorkers()
Ffmpeg = newFfmpegCli()
doneEnv := test_utils.SetEnv("FAIL_WITH", "expect failure")
doneEnv := test_env.SetEnv("FAIL_WITH", "expect failure")
defer doneEnv()
err := executable_worker.Ffmpeg.EncodeMp4("input", "output")
err := Ffmpeg.EncodeMp4("input", "output")
if err == nil {
t.Fatalf("Ffmpeg.EncodeMp4(...) = nil, should be an error.")
}
@@ -113,18 +163,18 @@ func TestFfmpegWithHWAcc(t *testing.T) {
}
func TestFfmpegWithCustomCOdec(t *testing.T) {
doneCodec := test_utils.SetEnv(utils.EnvVideoHardwareAcceleration.GetName(), "_custom")
doneCodec := test_env.SetEnv(utils.EnvVideoHardwareAcceleration.GetName(), "_custom")
defer doneCodec()
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
executable_worker.InitializeExecutableWorkers()
Ffmpeg = newFfmpegCli()
doneEnv := test_utils.SetEnv("FAIL_WITH", "expect failure")
doneEnv := test_env.SetEnv("FAIL_WITH", "expect failure")
defer doneEnv()
err := executable_worker.Ffmpeg.EncodeMp4("input", "output")
err := Ffmpeg.EncodeMp4("input", "output")
if err == nil {
t.Fatalf("Ffmpeg.EncodeMp4(...) = nil, should be an error.")
}

View File

@@ -2,36 +2,43 @@ package executable_worker
import (
"fmt"
"log"
"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.Printf("Executable worker disabled (%s=%q): ImageMagick\n", utils.EnvDisableRawProcessing.GetName(), utils.EnvDisableRawProcessing.GetValue())
return nil
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.Println("Executable worker not found: magick")
return nil
log.Error("Executable magick worker not found")
return &MagickCli{
err: ErrNoDependency,
}
}
version, err := exec.Command(path, "-version").Output()
if err != nil {
log.Printf("Error getting version of magick: %s\n", err)
return nil
log.Error("Executable magick worker get version error", "error", err)
return &MagickCli{
err: ErrNoDependency,
}
}
log.Printf("Found executable worker: magick (%s)\n", strings.Split(string(version), "\n")[0])
log.Info("Found magick executable worker", "version", strings.Split(string(version), "\n")[0])
return &MagickCli{
path: path,
@@ -39,10 +46,14 @@ func newMagickCli() *MagickCli {
}
func (cli *MagickCli) IsInstalled() bool {
return cli != nil
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",

View File

@@ -1,49 +1,90 @@
package executable_worker_test
package executable_worker
import (
"errors"
"regexp"
"testing"
"github.com/photoview/photoview/api/scanner/media_encoding/executable_worker"
"github.com/photoview/photoview/api/test_utils"
"github.com/photoview/photoview/api/test_utils/test_env"
)
func TestMagickCliNotExist(t *testing.T) {
done := test_utils.SetPathWithCurrent()
done := test_env.SetPathWithCurrent()
defer done()
executable_worker.InitializeExecutableWorkers()
if executable_worker.Magick.IsInstalled() {
t.Error("MagickCli should not be installed, but is found:", executable_worker.Magick)
Magick = newMagickCli()
if got, want := Magick.err, ErrNoDependency; got != want {
t.Errorf("Magick.err = %v, want: %v", got, want)
}
if Magick.IsInstalled() {
t.Error("MagickCli should not be installed, but is found:", Magick)
}
if got, want := Magick.EncodeJpeg("input", "output", 70), ErrNoDependency; !errors.Is(got, want) {
t.Errorf("Magick.EncodeJpeg() = %v, want: %v", got, want)
}
}
func TestMagickCliIgnore(t *testing.T) {
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
doneDisableRaw := test_utils.SetEnv("PHOTOVIEW_DISABLE_RAW_PROCESSING", "true")
doneDisableRaw := test_env.SetEnv("PHOTOVIEW_DISABLE_RAW_PROCESSING", "true")
defer doneDisableRaw()
executable_worker.InitializeExecutableWorkers()
if executable_worker.Magick.IsInstalled() {
t.Error("MagickCli should not be installed, but is found:", executable_worker.Magick)
Magick = newMagickCli()
if got, want := Magick.err, ErrDisabledFunction; got != want {
t.Errorf("Magick.err = %v, want: %v", got, want)
}
if Magick.IsInstalled() {
t.Error("MagickCli should not be installed, but is found:", Magick)
}
if got, want := Magick.EncodeJpeg("input", "output", 70), ErrDisabledFunction; !errors.Is(got, want) {
t.Errorf("Magick.EncodeJpeg() = %v, want: %v", got, want)
}
}
func TestMagickCliVersionFail(t *testing.T) {
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
done := test_env.SetEnv("FAIL_WITH", "failure")
defer done()
Magick = newMagickCli()
if got, want := Magick.err, ErrNoDependency; got != want {
t.Errorf("Magick.err = %v, want: %v", got, want)
}
if Magick.IsInstalled() {
t.Error("MagickCli should not be installed, but is found:", Magick)
}
if got, want := Magick.EncodeJpeg("input", "output", 70), ErrNoDependency; !errors.Is(got, want) {
t.Errorf("Magick.EncodeJpeg() = %v, want: %v", got, want)
}
}
func TestMagickCliFail(t *testing.T) {
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
executable_worker.InitializeExecutableWorkers()
if !executable_worker.Magick.IsInstalled() {
Magick = newMagickCli()
if !Magick.IsInstalled() {
t.Fatal("MagickCli should be installed")
}
done := test_utils.SetEnv("FAIL_WITH", "failure")
done := test_env.SetEnv("FAIL_WITH", "failure")
defer done()
err := executable_worker.Magick.EncodeJpeg("input", "output", 70)
err := Magick.EncodeJpeg("input", "output", 70)
if err == nil {
t.Fatalf(`MagickCli.EncodeJpeg(...) = nil, should be an error.`)
}
@@ -54,16 +95,17 @@ func TestMagickCliFail(t *testing.T) {
}
func TestMagickCliSucceed(t *testing.T) {
donePath := test_utils.SetPathWithCurrent(testdataBinPath)
donePath := test_env.SetPathWithCurrent(testdataBinPath)
defer donePath()
executable_worker.InitializeExecutableWorkers()
if !executable_worker.Magick.IsInstalled() {
Magick = newMagickCli()
if !Magick.IsInstalled() {
t.Fatal("MagickCli should be installed")
}
t.Run("Succeeded", func(t *testing.T) {
err := executable_worker.Magick.EncodeJpeg("input", "output", 70)
err := Magick.EncodeJpeg("input", "output", 70)
if err != nil {
t.Fatalf("MagickCli.EncodeJpeg(...) = %v, should be nil.", err)
}

View File

@@ -6,7 +6,6 @@ set -eu
if [ "$1" = "-version" ]
then
echo magick: version fake
exit 0
fi
if [ "${FAIL_WITH}" != "" ]