From 6836fea857f1c6c4f378e63151050091d883dda0 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Tue, 13 Jun 2017 17:06:30 +0200 Subject: [PATCH] Added MAL avatar source --- jobs/avatars/Avatar.go | 32 ++++++++++++++++++++++++ jobs/avatars/Gravatar.go | 34 ++++++++----------------- jobs/avatars/MyAnimeList.go | 50 +++++++++++++++++++++++++++++++++++++ jobs/avatars/main.go | 29 +++++++++++++-------- 4 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 jobs/avatars/MyAnimeList.go diff --git a/jobs/avatars/Avatar.go b/jobs/avatars/Avatar.go index b222c07b..80ba48ce 100644 --- a/jobs/avatars/Avatar.go +++ b/jobs/avatars/Avatar.go @@ -1,9 +1,12 @@ package main import ( + "bytes" + "fmt" "image" "github.com/animenotifier/arn" + "github.com/parnurzeal/gorequest" ) // Avatar represents a single image and the name of the format. @@ -13,3 +16,32 @@ type Avatar struct { Data []byte Format string } + +// String returns a text representation of the format, width and height. +func (avatar *Avatar) String() string { + return fmt.Sprint(avatar.Format, " | ", avatar.Image.Bounds().Dx(), "x", avatar.Image.Bounds().Dy()) +} + +// AvatarFromURL downloads and decodes the image from an URL and creates an Avatar. +func AvatarFromURL(url string, user *arn.User) *Avatar { + // Download + response, data, networkErr := gorequest.New().Get(url).EndBytes() + + if networkErr != nil || response.StatusCode != 200 { + return nil + } + + // Decode + img, format, decodeErr := image.Decode(bytes.NewReader(data)) + + if decodeErr != nil { + return nil + } + + return &Avatar{ + User: user, + Image: img, + Data: data, + Format: format, + } +} diff --git a/jobs/avatars/Gravatar.go b/jobs/avatars/Gravatar.go index dedc5f7f..7389e230 100644 --- a/jobs/avatars/Gravatar.go +++ b/jobs/avatars/Gravatar.go @@ -1,17 +1,18 @@ package main import ( - "bytes" "fmt" - "image" + "time" "github.com/animenotifier/arn" - "github.com/parnurzeal/gorequest" gravatar "github.com/ungerik/go-gravatar" ) // Gravatar - https://gravatar.com/ -type Gravatar struct{} +type Gravatar struct { + Rating string + RequestLimiter *time.Ticker +} // GetAvatar returns the Gravatar image for a user (if available). func (source *Gravatar) GetAvatar(user *arn.User) *Avatar { @@ -21,26 +22,11 @@ func (source *Gravatar) GetAvatar(user *arn.User) *Avatar { } // Build URL - gravatarURL := gravatar.Url(user.Email) + "?s=" + fmt.Sprint(arn.AvatarMaxSize) + "&d=404&r=pg" + gravatarURL := gravatar.Url(user.Email) + "?s=" + fmt.Sprint(arn.AvatarMaxSize) + "&d=404&r=" + source.Rating + + // Wait for request limiter to allow us to send a request + <-source.RequestLimiter.C // Download - response, data, networkErr := gorequest.New().Get(gravatarURL).EndBytes() - - if networkErr != nil || response.StatusCode != 200 { - return nil - } - - // Decode - img, format, decodeErr := image.Decode(bytes.NewReader(data)) - - if decodeErr != nil { - return nil - } - - return &Avatar{ - User: user, - Image: img, - Data: data, - Format: format, - } + return AvatarFromURL(gravatarURL, user) } diff --git a/jobs/avatars/MyAnimeList.go b/jobs/avatars/MyAnimeList.go new file mode 100644 index 00000000..fd4cedb6 --- /dev/null +++ b/jobs/avatars/MyAnimeList.go @@ -0,0 +1,50 @@ +package main + +import ( + "regexp" + "time" + + "github.com/animenotifier/arn" + "github.com/parnurzeal/gorequest" +) + +var userIDRegex = regexp.MustCompile(`(\d+)<\/user_id>`) + +// MyAnimeList - https://myanimelist.net/ +type MyAnimeList struct { + RequestLimiter *time.Ticker +} + +// GetAvatar returns the Gravatar image for a user (if available). +func (source *MyAnimeList) GetAvatar(user *arn.User) *Avatar { + malNick := user.Accounts.MyAnimeList.Nick + + // If the user has no username we can't get an avatar. + if malNick == "" { + return nil + } + + // Download user info + userInfoURL := "https://myanimelist.net/malappinfo.php?u=" + malNick + response, xml, networkErr := gorequest.New().Get(userInfoURL).End() + + if networkErr != nil || response.StatusCode != 200 { + return nil + } + + // Build URL + matches := userIDRegex.FindStringSubmatch(xml) + + if matches == nil || len(matches) < 2 { + return nil + } + + malID := matches[1] + malAvatarURL := "https://myanimelist.cdn-dena.com/images/userimages/" + malID + ".jpg" + + // Wait for request limiter to allow us to send a request + <-source.RequestLimiter.C + + // Download + return AvatarFromURL(malAvatarURL, user) +} diff --git a/jobs/avatars/main.go b/jobs/avatars/main.go index af0ec884..d2aa0c76 100644 --- a/jobs/avatars/main.go +++ b/jobs/avatars/main.go @@ -16,8 +16,7 @@ import ( ) const ( - networkRateLimit = 100 * time.Millisecond - webPQuality = 80 + webPQuality = 80 ) var avatarSources []AvatarSource @@ -30,24 +29,37 @@ func main() { // Define the avatar sources avatarSources = []AvatarSource{ - &Gravatar{}, + &Gravatar{ + Rating: "pg", + RequestLimiter: time.NewTicker(250 * time.Millisecond), + }, + &MyAnimeList{ + RequestLimiter: time.NewTicker(500 * time.Millisecond), + }, } // Define the avatar outputs avatarOutputs = []AvatarOutput{ + // Original - Large &AvatarOriginalFileOutput{ Directory: "images/avatars/large/original/", Size: arn.AvatarMaxSize, }, + + // Original - Small &AvatarOriginalFileOutput{ Directory: "images/avatars/small/original/", Size: arn.AvatarSmallSize, }, + + // WebP - Large &AvatarWebPFileOutput{ Directory: "images/avatars/large/webp/", Size: arn.AvatarMaxSize, Quality: webPQuality, }, + + // WebP - Small &AvatarWebPFileOutput{ Directory: "images/avatars/small/webp/", Size: arn.AvatarSmallSize, @@ -60,7 +72,7 @@ func main() { // Worker queue usersQueue := make(chan *arn.User) - StartWorkers(usersQueue, networkRateLimit, Work) + StartWorkers(usersQueue, Work) // We'll send each user to one of the worker threads for user := range users { @@ -69,13 +81,10 @@ func main() { } // StartWorkers creates multiple workers to handle a user each. -func StartWorkers(queue chan *arn.User, rateLimit time.Duration, work func(*arn.User)) { - rateLimiter := time.NewTicker(rateLimit) - +func StartWorkers(queue chan *arn.User, work func(*arn.User)) { for w := 0; w < runtime.NumCPU(); w++ { go func() { for user := range queue { - <-rateLimiter.C work(user) } }() @@ -90,7 +99,7 @@ func Work(user *arn.User) { avatar := source.GetAvatar(user) if avatar == nil { - fmt.Println(color.RedString("✘"), reflect.TypeOf(source).Elem().Name(), user.Nick) + // fmt.Println(color.RedString("✘"), reflect.TypeOf(source).Elem().Name(), user.Nick) continue } @@ -102,7 +111,7 @@ func Work(user *arn.User) { } } - fmt.Println(color.GreenString("✔"), user.Nick, "|", avatar.Format, avatar.Image.Bounds().Dx(), avatar.Image.Bounds().Dy()) + fmt.Println(color.GreenString("✔"), reflect.TypeOf(source).Elem().Name(), "|", user.Nick, "|", avatar) user.Avatar = "/+" + user.Nick + "/avatar" break }