implemented variable 'SLIDESHOW_TMPFILE' to lower memory consumption
This commit is contained in:
parent
acc7d1513b
commit
5eb3fd0ea1
6 changed files with 119 additions and 17 deletions
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
|
@ -11,7 +11,8 @@
|
|||
"SLIDESHOW_INTERVAL": "10",
|
||||
"SLIDESHOW_DIRECTORY": "/home/velvettear/images",
|
||||
"SLIDESHOW_SCANINTERVAL": "10",
|
||||
"SLIDESHOW_RESOLUTION": "600x1024",
|
||||
"SLIDESHOW_TMPFILE": "/tmp/.slideshow.img",
|
||||
"SLIDESHOW_RESOLUTION": "",
|
||||
"SLIDESHOW_LOGLEVEL": "debug",
|
||||
"SLIDESHOW_PALETTE": "/tmp/.slideshow.palette",
|
||||
"SLIDESHOW_PALETTE_ALGORITHM": "wsm",
|
||||
|
|
|
@ -15,7 +15,8 @@ configuration is entirely done via environment variables.
|
|||
| --------------------------- | ----------------- | -----------------------------------------------------------|
|
||||
| SLIDESHOW_INTERVAL | 60 | the interval of the slideshow in seconds |
|
||||
| SLIDESHOW_DIRECTORY | "$HOME" | path to a directory containing images |
|
||||
| SLIDESHOW_SCANINTERVAL | 60 | the interval for directory scans in seconds
|
||||
| SLIDESHOW_SCANINTERVAL | 60 | the interval for directory scans in seconds |
|
||||
| SLIDESHOW_TMPFILE | | path to a temporary file |
|
||||
| SLIDESHOW_RESOLUTION | | the resolution to which images are scaled (i.e. 1920x1080) |
|
||||
| SLIDESHOW_PALETTE | | path to a file where the color palette will be stored |
|
||||
| SLIDESHOW_PALETTE_ALGORITHM | "wsm" | the algorithm used to generate the color palette |
|
||||
|
@ -23,7 +24,8 @@ configuration is entirely done via environment variables.
|
|||
| SLIDESHOW_LOGLEVEL | "info" | the log level |
|
||||
|
||||
**note:**
|
||||
if no resolution is set the images will be displayed as they are (without any scaling).
|
||||
- if `SLIDESHOW_RESOLUTION` is unset the images will be displayed as they are (without any scaling).
|
||||
- if `SLIDESHOW_TMPFILE` is set a temporary file will be used instead of keeping the image internally buffered. this could be useful on systems with limited ram.
|
||||
|
||||
**available log levels:**
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
var Interval time.Duration
|
||||
var Directory string
|
||||
var ScanInterval time.Duration
|
||||
var TmpFile string
|
||||
var Resolution string
|
||||
var PaletteFile string
|
||||
var PaletteAlgorithm int
|
||||
|
@ -45,6 +46,7 @@ func Initialize() {
|
|||
tmpInt = 60
|
||||
}
|
||||
ScanInterval = time.Duration(tmpInt) * time.Second
|
||||
TmpFile = os.Getenv("SLIDESHOW_TMPFILE")
|
||||
Resolution = os.Getenv("SLIDESHOW_RESOLUTION")
|
||||
if len(Resolution) > 0 {
|
||||
width, height, found := strings.Cut(Resolution, "x")
|
||||
|
@ -83,3 +85,8 @@ func IsResolutionSet() bool {
|
|||
func IsPaletteSet() bool {
|
||||
return len(PaletteFile) > 0
|
||||
}
|
||||
|
||||
// check if a temporary file has been specified
|
||||
func IsTemporaryFileSet() bool {
|
||||
return len(TmpFile) > 0
|
||||
}
|
||||
|
|
|
@ -62,6 +62,23 @@ func getColorPaletteRaw(data []byte) ([]color.Color, error) {
|
|||
return colors, error
|
||||
}
|
||||
|
||||
// extract the given amount of dominant colors of an image
|
||||
func getColorPalette(image string) ([]color.Color, error) {
|
||||
var colors []color.Color
|
||||
if !config.IsPaletteSet() {
|
||||
return colors, nil
|
||||
}
|
||||
timestamp := time.Now().UnixMilli()
|
||||
amount := config.PaletteColors
|
||||
colors, error := color_thief.GetPaletteFromFile(image, amount, config.PaletteAlgorithm)
|
||||
if error != nil {
|
||||
loggo.ErrorTimed("encountered an error generating the color palette from image", timestamp, "image: "+image, "colors: "+strconv.Itoa(amount))
|
||||
} else {
|
||||
loggo.DebugTimed("generated color palette from image", timestamp, "image: "+image, "colors: "+strconv.Itoa(amount))
|
||||
}
|
||||
return colors, error
|
||||
}
|
||||
|
||||
// parse rgb color values to hex
|
||||
func rgbToHex(color color.Color) string {
|
||||
r, g, b, _ := color.RGBA()
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"errors"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -13,9 +12,12 @@ import (
|
|||
)
|
||||
|
||||
// scale an image
|
||||
func scale(image string) ([]byte, error) {
|
||||
func scale(image string, output string) ([]byte, error) {
|
||||
timestamp := time.Now().UnixMilli()
|
||||
cmd := exec.Command("convert", image, "-resize", config.Resolution+"^", "-gravity", "center", "-extent", config.Resolution, "-")
|
||||
if len(output) == 0 {
|
||||
output = "-"
|
||||
}
|
||||
cmd := exec.Command("convert", image, "-resize", config.Resolution+"^", "-gravity", "center", "-extent", config.Resolution, output)
|
||||
stdout, stdoutError := cmd.StdoutPipe()
|
||||
stderr, stderrError := cmd.StderrPipe()
|
||||
var data []byte
|
||||
|
@ -39,6 +41,6 @@ func scale(image string) ([]byte, error) {
|
|||
if len(errorMessage) > 0 {
|
||||
return data, errors.New(errorMessage)
|
||||
}
|
||||
loggo.DebugTimed("successfully scaled image", timestamp, "image: "+image, "resolution: "+config.Resolution, "size: "+strconv.Itoa(len(data)))
|
||||
loggo.DebugTimed("successfully scaled image", timestamp, "image: "+image, "resolution: "+config.Resolution)
|
||||
return data, nil
|
||||
}
|
||||
|
|
|
@ -43,30 +43,75 @@ func Start() {
|
|||
}
|
||||
}
|
||||
if config.IsResolutionSet() {
|
||||
tmp, error := scale(image)
|
||||
tmp, error := scale(image, config.TmpFile)
|
||||
if error != nil {
|
||||
loggo.Error("encountered an error scaling an image", "image: "+image, error.Error())
|
||||
continue
|
||||
}
|
||||
data = tmp
|
||||
} else {
|
||||
if !config.IsTemporaryFileSet() {
|
||||
tmp, error := os.ReadFile(image)
|
||||
if error != nil {
|
||||
loggo.Error("encountered an erro reading an image", "image: "+image, error.Error())
|
||||
loggo.Error("encountered an error reading an image", "image: "+image, error.Error())
|
||||
continue
|
||||
}
|
||||
data = tmp
|
||||
} else {
|
||||
file, error := os.Open(image)
|
||||
if error != nil {
|
||||
loggo.Error("encountered an error opening an image", "image: "+image, error.Error())
|
||||
continue
|
||||
}
|
||||
defer file.Close()
|
||||
tmpFile, error := os.Create(config.TmpFile)
|
||||
if error != nil {
|
||||
loggo.Error("encountered an error opening the temporary file", "path: "+config.TmpFile, error.Error())
|
||||
continue
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
var errorMessage string
|
||||
buffer := make([]byte, 4096)
|
||||
for {
|
||||
read, error := file.Read(buffer)
|
||||
if error != nil && error != io.EOF {
|
||||
errorMessage = error.Error()
|
||||
break
|
||||
}
|
||||
if read == 0 {
|
||||
break
|
||||
}
|
||||
_, error = tmpFile.Write(buffer[:read])
|
||||
if error != nil {
|
||||
errorMessage = error.Error()
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(errorMessage) > 0 {
|
||||
loggo.Error("encountered an error reading an image", "image: "+image, errorMessage)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if config.IsTemporaryFileSet() {
|
||||
palette, _ = getColorPalette(config.TmpFile)
|
||||
} else {
|
||||
palette, _ = getColorPaletteRaw(data)
|
||||
}
|
||||
loopTime = time.Since(loopTimestamp)
|
||||
if sleepTime > 0 {
|
||||
loggo.Debug("sleeping for " + strconv.FormatInt(sleepTime.Milliseconds(), 10) + "ms before next image will be displayed...")
|
||||
time.Sleep(sleepTime)
|
||||
}
|
||||
go exportPalette(palette)
|
||||
error := setBackground(data)
|
||||
if error != nil {
|
||||
loggo.Error("encountered an error setting the background image", error.Error())
|
||||
var err error
|
||||
if config.IsTemporaryFileSet() {
|
||||
err = setBackgroundImage(config.TmpFile)
|
||||
} else {
|
||||
err = setBackground(data)
|
||||
}
|
||||
if err != nil {
|
||||
loggo.Error("encountered an error setting the background image", err.Error())
|
||||
}
|
||||
loggo.Info("set new background image", "image: "+image)
|
||||
sleepTime = config.Interval - loopTime
|
||||
|
@ -113,3 +158,31 @@ func setBackground(data []byte) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// set the background image via 'feh'
|
||||
func setBackgroundImage(image string) error {
|
||||
cmd := exec.Command("feh", "--no-fehbg", "--bg-fill", image)
|
||||
stdout, stdoutError := cmd.StdoutPipe()
|
||||
stderr, stderrError := cmd.StderrPipe()
|
||||
cmd.Start()
|
||||
if stdoutError != nil {
|
||||
return stdoutError
|
||||
}
|
||||
if stderrError != nil {
|
||||
return stderrError
|
||||
}
|
||||
_, 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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue