Initial working prototype
This commit is contained in:
parent
46a865b97c
commit
b5a29f1f54
10 changed files with 437 additions and 1 deletions
196
main.go
196
main.go
|
|
@ -1,5 +1,199 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"golang.org/x/image/draw"
|
||||
|
||||
"git.chandlerswift.com/chandlerswift/bannergen/internal/datafetchers"
|
||||
"git.chandlerswift.com/chandlerswift/bannergen/internal/datafetchers/espn"
|
||||
"git.chandlerswift.com/chandlerswift/bannergen/internal/types"
|
||||
)
|
||||
|
||||
//go:embed static templates
|
||||
var frontend embed.FS
|
||||
|
||||
func main() {
|
||||
port := flag.Int("port", 8000, "Port to listen on")
|
||||
flag.Parse()
|
||||
|
||||
// preload teams data
|
||||
fetchers := []datafetchers.DataFetcher{
|
||||
espn.ESPNDataFetcher{},
|
||||
}
|
||||
var sports []types.Sport
|
||||
for _, fetcher := range fetchers {
|
||||
sports = append(sports, fetcher.Fetch()...)
|
||||
}
|
||||
|
||||
rawBuildInfo, _ := debug.ReadBuildInfo()
|
||||
var revision, time string
|
||||
var dirty bool
|
||||
for _, buildSetting := range rawBuildInfo.Settings {
|
||||
if buildSetting.Key == "vcs.revision" {
|
||||
revision = buildSetting.Value
|
||||
} else if buildSetting.Key == "vcs.time" {
|
||||
time = buildSetting.Value
|
||||
} else if buildSetting.Key == "vcs.modified" {
|
||||
dirty = buildSetting.Value == "true"
|
||||
}
|
||||
}
|
||||
buildInfo := map[string]interface{}{
|
||||
"GitRev": revision,
|
||||
"GitDirty": dirty,
|
||||
"GitTime": time,
|
||||
"GoVersion": rawBuildInfo.GoVersion,
|
||||
"AllBuildInfo": rawBuildInfo.String(),
|
||||
}
|
||||
|
||||
http.Handle("GET /static/", http.FileServer(http.FS(frontend)))
|
||||
tmpls := template.Must(template.ParseFS(frontend, "templates/*"))
|
||||
http.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
|
||||
err := tmpls.ExecuteTemplate(w, "index.html", map[string]interface{}{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
http.HandleFunc("GET /about.html", func(w http.ResponseWriter, r *http.Request) {
|
||||
err := tmpls.ExecuteTemplate(w, "about.html", map[string]interface{}{
|
||||
"buildInfo": buildInfo,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
|
||||
http.HandleFunc("/generate/{image_name}", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
colorLeftHex := r.Form.Get("left_color")
|
||||
if colorLeftHex == "" {
|
||||
http.Error(w, "no left_color provided", 400)
|
||||
}
|
||||
colorRightHex := r.Form.Get("right_color")
|
||||
if colorRightHex == "" {
|
||||
http.Error(w, "no right_color provided", 400)
|
||||
}
|
||||
logoLeftPath := r.Form.Get("left_logo")
|
||||
if logoLeftPath == "" {
|
||||
http.Error(w, "no left_logo provided", 400)
|
||||
}
|
||||
logoRightPath := r.Form.Get("right_logo")
|
||||
if logoRightPath == "" {
|
||||
http.Error(w, "no right_logo provided", 400)
|
||||
}
|
||||
|
||||
size := 400 // TODO: param
|
||||
|
||||
// Set image dimensions; TODO: param
|
||||
const width, height = 1280, 720
|
||||
|
||||
// Create a blank RGBA image
|
||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
|
||||
// Convert hex colors to RGBA
|
||||
colorLeft := hexToRGBA(colorLeftHex)
|
||||
colorRight := hexToRGBA(colorRightHex)
|
||||
|
||||
// Fill the image with colors split by angle
|
||||
centerX, centerY := float64(width)/2, float64(height)/2
|
||||
for x := 0; x < width; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
dx, dy := float64(x)-centerX, float64(y)-centerY
|
||||
rotatedAngle := math.Atan2(dy, dx) - 16.0*math.Pi/180
|
||||
if math.Cos(rotatedAngle) >= 0 {
|
||||
img.Set(x, y, colorRight)
|
||||
} else {
|
||||
img.Set(x, y, colorLeft)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load logos
|
||||
logoLeft, err := loadPNG(logoLeftPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logoRight, err := loadPNG(logoRightPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Resize logos using golang.org/x/image/draw
|
||||
logoLeftResized := resizeImage(logoLeft, size, size)
|
||||
logoRightResized := resizeImage(logoRight, size, size)
|
||||
|
||||
// Place logos
|
||||
logoLeftX, logoLeftY := (width/4)-(size/2), (height/2)-(size/2)
|
||||
logoRightX, logoRightY := (3*width/4)-(size/2), (height/2)-(size/2)
|
||||
draw.Draw(img, image.Rect(logoLeftX, logoLeftY, logoLeftX+size, logoLeftY+size), logoLeftResized, image.Point{}, draw.Over)
|
||||
draw.Draw(img, image.Rect(logoRightX, logoRightY, logoRightX+size, logoRightY+size), logoRightResized, image.Point{}, draw.Over)
|
||||
|
||||
png.Encode(w, img)
|
||||
})
|
||||
|
||||
panic(http.ListenAndServe(fmt.Sprintf(":%v", *port), nil))
|
||||
}
|
||||
|
||||
func resizeImage(src *image.RGBA, newWidth, newHeight int) *image.RGBA {
|
||||
dst := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
|
||||
draw.CatmullRom.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil)
|
||||
return dst
|
||||
}
|
||||
|
||||
func hexToRGBA(hex string) color.RGBA {
|
||||
var r, g, b int
|
||||
var err error
|
||||
if hex[0] == '#' {
|
||||
_, err = fmt.Sscanf(hex, "#%02x%02x%02x", &r, &g, &b)
|
||||
} else {
|
||||
_, err = fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return color.RGBA{uint8(r), uint8(g), uint8(b), 255}
|
||||
}
|
||||
|
||||
func loadPNG(path string) (*image.RGBA, error) {
|
||||
var reader io.ReadCloser
|
||||
var err error
|
||||
|
||||
// TODO: smarter limits here
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", path, nil)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("failed to fetch image: %s", resp.Status)
|
||||
}
|
||||
reader = resp.Body
|
||||
defer reader.Close()
|
||||
|
||||
img, err := png.Decode(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to RGBA
|
||||
rgbaImg := image.NewRGBA(img.Bounds())
|
||||
draw.Draw(rgbaImg, rgbaImg.Bounds(), img, image.Point{}, draw.Src)
|
||||
return rgbaImg, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue