package api import ( "errors" "io" "net/http" "os" "os/exec" "strconv" "strings" "time" "git.velvettear.de/velvettear/loggo" "git.velvettear.de/velvettear/slideshow-api/internal" "git.velvettear.de/velvettear/slideshow-api/internal/config" color_thief "github.com/kennykarnama/color-thief" ) // central streaming method for images func streamImage(writer http.ResponseWriter, image string) { if config.IsResolutionSet() { streamScaledImage(writer, image) return } streamUnscaledImage(writer, image) } // stream a color palette func streamColorPalette(writer http.ResponseWriter, image string) { timestamp := time.Now().UnixMilli() var response response amount := config.PaletteColors colors, error := color_thief.GetPaletteFromFile(image, amount, config.PaletteAlgorithm) if error != nil { loggo.Error("encountered an error getting the color palette from image '"+image+"'", error.Error()) response.error = error response.send(writer) return } var palette string for index, color := range colors { if index > 0 { palette += "\n" } tmp := strconv.Itoa(index) if len(tmp) < 2 { tmp = "0" + tmp } tmp = "SLIDESHOW_COLOR" + tmp palette += tmp + "=\"" + internal.RgbToHex(color) + "\"" } data := []byte(palette) writer.Write(data) loggo.InfoTimed("successfully streamed color palette for image '"+image+"'", timestamp, "size: "+internal.FormatBytes(int64(len(data)))) } // stream an unscaled image func streamUnscaledImage(writer http.ResponseWriter, image string) { timestamp := time.Now().UnixMilli() var response response file, error := os.Open(image) if error != nil { loggo.Error("encountered an error opening image '"+image+"' for streaming", error.Error()) response.error = error response.send(writer) return } defer file.Close() written, error := io.Copy(writer, file) if error != nil { loggo.Error("encountered an error piping image '"+image+"' to stream", error.Error()) response.error = error response.send(writer) return } loggo.InfoTimed("successfully streamed image '"+image+"'", timestamp, "size: "+internal.FormatBytes(written)) } // scale and stream an image func streamScaledImage(writer http.ResponseWriter, image string) { timestamp := time.Now().UnixMilli() var response response cmd := exec.Command("convert", image, "-resize", config.Resolution+"^", "-gravity", "center", "-extent", config.Resolution, "-") stdout, stdoutError := cmd.StdoutPipe() stderr, stderrError := cmd.StderrPipe() cmd.Start() if stdoutError != nil { loggo.Error("enountered an error opening imagemagicks's stdout pipe", stdoutError.Error()) response.error = stdoutError response.send(writer) return } if stderrError != nil { loggo.Error("enountered an error opening imagemagicks's stderr pipe", stderrError.Error()) response.error = stderrError response.send(writer) return } written, copyError := io.Copy(writer, stdout) if copyError != nil { loggo.Error("enountered an error piping imagemagicks's output to the stream", copyError.Error()) response.error = copyError response.send(writer) return } if written == 0 { errorBytes, stderrError := io.ReadAll(stderr) if stderrError != nil { loggo.Error("enountered an error reading imagemagicks's stderr", stderrError.Error()) response.error = stderrError response.send(writer) return } if len(errorBytes) > 0 { error := errors.New(strings.TrimSpace(string(errorBytes))) loggo.Error("enountered an error executing imagemagick", error.Error()) response.error = error response.send(writer) return } } cmd.Wait() loggo.InfoTimed("successfully streamed scaled image '"+image+"'", timestamp, "size: "+internal.FormatBytes(written), "resolution: "+config.Resolution) }