From 586befcb1a407f95a8649e0dabc21bf095e1371d Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Mon, 12 Jun 2017 20:06:31 +0200 Subject: [PATCH] Avatar downloader --- images/avatars/.gitignore | 2 + images/elements/no-gravatar.svg | 4 + jobs/avatars/avatars.go | 122 +++++++++++++++++++++ jobs/avatars/webp.go | 40 +++++++ main.go | 4 + mixins/ProfileImage.pixy | 6 +- pages/profile/profile.scarlet | 4 +- patches/user-references/user-references.go | 4 +- styles/svg.scarlet | 5 + 9 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 images/avatars/.gitignore create mode 100644 images/elements/no-gravatar.svg create mode 100644 jobs/avatars/avatars.go create mode 100644 jobs/avatars/webp.go create mode 100644 styles/svg.scarlet diff --git a/images/avatars/.gitignore b/images/avatars/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/images/avatars/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/images/elements/no-gravatar.svg b/images/elements/no-gravatar.svg new file mode 100644 index 00000000..57d2df01 --- /dev/null +++ b/images/elements/no-gravatar.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/jobs/avatars/avatars.go b/jobs/avatars/avatars.go new file mode 100644 index 00000000..9b7a5fb6 --- /dev/null +++ b/jobs/avatars/avatars.go @@ -0,0 +1,122 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "runtime" + "time" + + "github.com/animenotifier/arn" + "github.com/fatih/color" + "github.com/parnurzeal/gorequest" + gravatar "github.com/ungerik/go-gravatar" +) + +func main() { + users, _ := arn.AllUsers() + + usersQueue := make(chan *arn.User) + rateLimiter := time.NewTicker(100 * time.Millisecond) + defer rateLimiter.Stop() + + for w := 0; w < runtime.NumCPU(); w++ { + go func(workerID int) { + for user := range usersQueue { + <-rateLimiter.C + os.Stdout.WriteString("[" + fmt.Sprint(workerID) + "] ") + downloadAvatar(user) + makeWebPAvatar(user) + } + }(w) + } + + for user := range users { + usersQueue <- user + } +} + +func findAvatar(user *arn.User, dir string) string { + testExtensions := []string{"", ".jpg", ".png", ".gif", ".webp"} + + for _, testExt := range testExtensions { + if _, err := os.Stat(dir + user.ID + testExt); !os.IsNotExist(err) { + return user.ID + testExt + } + } + + return "" +} + +func makeWebPAvatar(user *arn.User) { + baseName := findAvatar(user, "../../images/avatars/original/") + + if baseName == "" { + return + } + + original := "../../images/avatars/original/" + baseName + outFile := "../../images/avatars/webp/" + user.ID + ".webp" + + err := convertFileToWebP(original, outFile, 80) + + if err != nil { + color.Red("[WebP] " + original + " -> " + outFile) + } else { + color.Green("[WebP] " + original + " -> " + outFile) + } +} + +func downloadAvatar(user *arn.User) { + if user.Email == "" { + return + } + + directory := "../../images/avatars/original/" + fileName := directory + user.ID + + // Build URL + url := gravatar.Url(user.Email) + "?s=560&d=404&r=pg" + + // Skip existing avatars + if findAvatar(user, directory) != "" { + color.Yellow(url) + return + } + + // Download + response, data, err := gorequest.New().Get(url).EndBytes() + + if err != nil { + color.Red(url) + return + } + + contentType := response.Header.Get("content-type") + + if response.StatusCode != 200 { + color.Red(url) + return + } + + color.Green(url) + + // Determine file extension + extension := "" + + switch contentType { + case "image/jpeg": + extension = ".jpg" + case "image/png": + extension = ".png" + case "image/gif": + extension = ".gif" + case "image/webp": + extension = ".webp" + } + + fileName += extension + + // Write to disk + ioutil.WriteFile(fileName, data, 0644) +} diff --git a/jobs/avatars/webp.go b/jobs/avatars/webp.go new file mode 100644 index 00000000..7706c0ca --- /dev/null +++ b/jobs/avatars/webp.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "image" + _ "image/gif" + _ "image/jpeg" + _ "image/png" + "os" + + "github.com/chai2010/webp" +) + +func convertFileToWebP(in string, out string, quality float32) error { + f, openErr := os.Open(in) + + if openErr != nil { + return openErr + } + + img, format, decodeErr := image.Decode(f) + + if decodeErr != nil { + return decodeErr + } + + fmt.Println(format, img.Bounds().Dx(), img.Bounds().Dy()) + + fileOut, writeErr := os.Create(out) + + if writeErr != nil { + return writeErr + } + + encodeErr := webp.Encode(fileOut, img, &webp.Options{ + Quality: quality, + }) + + return encodeErr +} diff --git a/main.go b/main.go index 40282074..b6d2e56a 100644 --- a/main.go +++ b/main.go @@ -76,6 +76,10 @@ func main() { return ctx.File("images/cover/" + ctx.Get("file") + format) }) + app.Get("/images/elements/:file", func(ctx *aero.Context) string { + return ctx.File("images/elements/" + ctx.Get("file")) + }) + // For benchmarks app.Get("/hello", func(ctx *aero.Context) string { return ctx.Text("Hello World") diff --git a/mixins/ProfileImage.pixy b/mixins/ProfileImage.pixy index 64da18e7..d45811c4 100644 --- a/mixins/ProfileImage.pixy +++ b/mixins/ProfileImage.pixy @@ -1,5 +1,7 @@ component ProfileImage(user *arn.User) if user.Avatar != "" - img.profile-image(src=user.Avatar + "?s=500&r=x&d=mm", alt="Profile image") + img.profile-image(src=user.Avatar + "?s=560&r=x&d=mm", alt="Profile image") else - img.profile-image(src="/images/elements/no-gravatar.svg", alt="Profile image") \ No newline at end of file + svg.profile-image(width=280, height=280, viewBox="0 0 50 50", alt="Profile image") + circle.head(cx="25", cy="19", r="10") + circle.body(cx="25", cy="50", r="20") \ No newline at end of file diff --git a/pages/profile/profile.scarlet b/pages/profile/profile.scarlet index d9450565..4387c9cc 100644 --- a/pages/profile/profile.scarlet +++ b/pages/profile/profile.scarlet @@ -66,8 +66,8 @@ animation cover-animation .image-container flex 1 - max-width 275px - max-height 275px + max-width 280px + max-height 280px border-radius 3px overflow hidden diff --git a/patches/user-references/user-references.go b/patches/user-references/user-references.go index a3b47a79..e9597a7d 100644 --- a/patches/user-references/user-references.go +++ b/patches/user-references/user-references.go @@ -8,8 +8,8 @@ import ( func main() { color.Yellow("Updating user references") - arn.Truncate("NickToUser") - arn.Truncate("EmailToUser") + arn.DB.DeleteTable("NickToUser") + arn.DB.DeleteTable("EmailToUser") // Get a stream of all anime allUsers, err := arn.AllUsers() diff --git a/styles/svg.scarlet b/styles/svg.scarlet new file mode 100644 index 00000000..a2b035ad --- /dev/null +++ b/styles/svg.scarlet @@ -0,0 +1,5 @@ +.head + fill text-color + +.body + fill text-color \ No newline at end of file