2023-11-29 16:00:35 +01:00
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
2023-11-29 16:27:23 +01:00
func streamImage ( writer http . ResponseWriter , image string , resolution string ) {
if config . ConvertAvailable && config . IsValidResolution ( resolution ) {
streamScaledImage ( writer , image , resolution )
2023-11-29 16:00:35 +01:00
return
}
streamUnscaledImage ( writer , image )
}
// stream a color palette
2023-11-29 16:43:30 +01:00
func streamColorPalette ( writer http . ResponseWriter , image string , amount int , algorithm string ) {
2023-11-29 16:00:35 +01:00
timestamp := time . Now ( ) . UnixMilli ( )
var response response
2023-11-29 16:43:30 +01:00
algo := 1
algorithm = strings . ToLower ( algorithm )
if algorithm == "wu" {
algo = 0
2023-11-30 14:16:39 +01:00
} else {
algorithm = "wsm"
2023-11-29 16:43:30 +01:00
}
colors , error := color_thief . GetPaletteFromFile ( image , amount , algo )
2023-11-29 16:00:35 +01:00
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 )
2023-11-29 16:43:30 +01:00
loggo . InfoTimed ( "successfully streamed color palette for image '" + image + "'" , timestamp , "size: " + internal . FormatBytes ( int64 ( len ( data ) ) ) , "colors: " + strconv . Itoa ( amount ) , "algorithm: " + algorithm )
2023-11-29 16:00:35 +01:00
}
// 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
2023-11-29 16:27:23 +01:00
func streamScaledImage ( writer http . ResponseWriter , image string , resolution string ) {
2023-11-29 16:00:35 +01:00
timestamp := time . Now ( ) . UnixMilli ( )
var response response
2023-11-29 16:27:23 +01:00
cmd := exec . Command ( "convert" , image , "-resize" , resolution + "^" , "-gravity" , "center" , "-extent" , resolution , "-" )
2023-11-29 16:00:35 +01:00
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 ( )
2023-11-29 16:27:23 +01:00
loggo . InfoTimed ( "successfully streamed scaled image '" + image + "'" , timestamp , "size: " + internal . FormatBytes ( written ) , "resolution: " + resolution )
2023-11-29 16:00:35 +01:00
}