From 8e245c04881f041efae66bf01981595ca318913c Mon Sep 17 00:00:00 2001 From: velvettear Date: Fri, 17 Nov 2023 13:09:17 +0100 Subject: [PATCH] pipe scaled image to feh's stdin instead of writing to a file --- .vscode/launch.json | 2 +- go.mod | 2 +- internal/config/config.go | 5 +++ internal/slideshow/scale.go | 25 +++++------ internal/slideshow/slideshow.go | 77 ++++++++++++++++++++++++--------- 5 files changed, 75 insertions(+), 36 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 132cc5d..da4c53c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "mode": "auto", "program": "${workspaceFolder}/main.go", "env":{ - "SLIDESHOW_INTERVAL": "13", + "SLIDESHOW_INTERVAL": "10", "SLIDESHOW_DIRECTORY": "/home/velvettear/images", "SLIDESHOW_RESOLUTION": "600x1024", "SLIDESHOW_LOGLEVEL": "debug" diff --git a/go.mod b/go.mod index fa71675..961f367 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require github.com/fsnotify/fsnotify v1.7.0 require ( github.com/fatih/color v1.16.0 // indirect - github.com/kennykarnama/color-thief v0.0.0-20230222041546-c1bf65ec0808 // indirect + github.com/kennykarnama/color-thief v0.0.0-20230222041546-c1bf65ec0808 github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect golang.org/x/sys v0.14.0 // indirect diff --git a/internal/config/config.go b/internal/config/config.go index 0b39512..42c0b4c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -53,3 +53,8 @@ func Initialize() { return } } + +// check if a resolution has been specified +func IsResolutionSet() bool { + return len(Resolution) > 0 +} diff --git a/internal/slideshow/scale.go b/internal/slideshow/scale.go index fac223d..f993e91 100644 --- a/internal/slideshow/scale.go +++ b/internal/slideshow/scale.go @@ -3,9 +3,7 @@ package slideshow import ( "errors" "io" - "os" "os/exec" - "path/filepath" "strconv" "strings" "time" @@ -15,33 +13,32 @@ import ( ) // scale an image -func scale(image string) (string, error) { +func scale(image string) ([]byte, error) { timestamp := time.Now().UnixMilli() - scaledImage := filepath.Join(os.TempDir(), strconv.FormatInt(timestamp, 10)+"_"+filepath.Base(image)) - arguments := []string{image, "-resize", config.Resolution + "^", "-gravity", "center", "-extent", config.Resolution, scaledImage} - cmd := exec.Command("convert", arguments...) + cmd := exec.Command("convert", image, "-resize", config.Resolution+"^", "-gravity", "center", "-extent", config.Resolution, "-") stdout, stdoutError := cmd.StdoutPipe() stderr, stderrError := cmd.StderrPipe() + var data []byte cmd.Start() if stdoutError != nil { - return scaledImage, stdoutError + return data, stdoutError } if stderrError != nil { - return scaledImage, stdoutError + return data, stdoutError } - _, stdoutError = io.ReadAll(stdout) + data, stdoutError = io.ReadAll(stdout) if stdoutError != nil { - return scaledImage, stdoutError + return data, stdoutError } errorBytes, stderrError := io.ReadAll(stderr) if stderrError != nil { - return scaledImage, stdoutError + return data, stdoutError } cmd.Wait() errorMessage := strings.TrimSpace(string(errorBytes)) if len(errorMessage) > 0 { - return scaledImage, errors.New(errorMessage) + return data, errors.New(errorMessage) } - loggo.DebugTimed("successfully scaled image", timestamp, "image: "+image, "scaled image: "+scaledImage, "resolution: "+config.Resolution) - return scaledImage, nil + loggo.DebugTimed("successfully scaled image", timestamp, "image: "+image, "resolution: "+config.Resolution, "size: "+strconv.Itoa(len(data))) + return data, nil } diff --git a/internal/slideshow/slideshow.go b/internal/slideshow/slideshow.go index 58669e9..976e98d 100644 --- a/internal/slideshow/slideshow.go +++ b/internal/slideshow/slideshow.go @@ -3,7 +3,6 @@ package slideshow import ( "errors" "io" - "os" "os/exec" "strconv" "strings" @@ -20,37 +19,34 @@ func Start() { loggo.Info("starting the image slideshow...", "interval: "+strconv.FormatFloat(config.Interval.Seconds(), 'f', 0, 64)+" seconds") var sleepTime time.Duration for { - var scaledImage string image := watcher.GetRandomImage() - if len(config.Resolution) > 0 { - tmp, error := scale(image) + if config.IsResolutionSet() { + data, error := scale(image) if error != nil { - loggo.Fatal("encountered an error scaling an image", "image: "+image, error.Error()) + loggo.Error("encountered an error scaling an image", "image: "+image, error.Error()) + continue } - scaledImage = tmp - image = scaledImage + error = setBackgroundPiped(data) + if error != nil { + loggo.Error("encountered an error setting the background via pipe to feh's stdin", error.Error()) + } + loggo.Info("set new scaled background image", "image: "+image, "resolution: "+config.Resolution) + } else { + error := setBackgroundImage(image) + if error != nil { + loggo.Error("encountered an error setting the background image", "image: "+image, error.Error()) + } + loggo.Info("set new background image", "image: "+image) } if sleepTime > 0 { time.Sleep(sleepTime) } sleepTime = time.Until(time.Now().Add(config.Interval)) - error := setBackground(image) - if error != nil { - loggo.Error("encountered an error setting the background image", "image: "+image, error.Error()) - } - loggo.Info("set new background image", "image: "+image) - if len(scaledImage) > 0 { - error = os.Remove(scaledImage) - if error != nil { - loggo.Error("encountered an error deleting a scaled image", "image: "+scaledImage) - } - } - } } // set the background image via 'feh' -func setBackground(image string) error { +func setBackgroundImage(image string) error { cmd := exec.Command("feh", "--no-fehbg", "--bg-fill", image) stdout, stdoutError := cmd.StdoutPipe() stderr, stderrError := cmd.StderrPipe() @@ -76,3 +72,44 @@ func setBackground(image string) error { } return nil } + +// pipe data to 'feh' via stdin to set the background image +func setBackgroundPiped(data []byte) error { + cmd := exec.Command("feh", "--no-fehbg", "--bg-fill", "-") + stdin, stdinError := cmd.StdinPipe() + stdout, stdoutError := cmd.StdoutPipe() + stderr, stderrError := cmd.StderrPipe() + cmd.Start() + if stdinError != nil { + return stdinError + } + if stdoutError != nil { + return stdoutError + } + if stderrError != nil { + return stderrError + } + defer stdin.Close() + defer stdout.Close() + defer stderr.Close() + _, stdinError = stdin.Write(data) + if stdinError != nil { + return stdinError + } else { + stdin.Close() + } + _, stdoutError = io.ReadAll(stdout) + if stdoutError != nil { + return stdoutError + } + errorBytes, stderrError := io.ReadAll(stderr) + if stderrError != nil { + return stderrError + } + cmd.Wait() + error := strings.TrimSpace(string(errorBytes)) + if len(error) > 0 { + return errors.New(error) + } + return nil +}