Implement Imgur info
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Tyler 2019-10-03 23:31:24 -04:00
parent 27e7dab209
commit 137e18e6c1
5 changed files with 187 additions and 6 deletions

View File

@ -7,3 +7,6 @@ steps:
image: golang image: golang
commands: commands:
- go test - go test
environment:
IMGUR_CLIENT_ID:
from_secret: imgur_client_id

135
imgur.go
View File

@ -1,9 +1,140 @@
package linkinfo package linkinfo
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"path/filepath"
"regexp"
"strings"
)
const imgurApiUrl = "https://api.imgur.com/3/%s/%s.json"
var ( var (
imgurHosts = []string{"imgur.com", "i.imgur.com"} imgurHosts = []string{"imgur.com", "i.imgur.com"}
imgurAlbumRegexp = regexp.MustCompile("^/(a|gallery)/")
) )
func (i *LinkInfoApi) ImgurLinkHandler(link string) (*LinkInfo, error) { type ImgurOptions struct {
return nil, nil Option
ClientId string
}
type ImgurInfoApi struct {
api *LinkInfoApi
opts *ImgurOptions
}
type imgurResponse struct {
Success bool `json:"success"`
Data imgurData `json:"data"`
}
type imgurData struct {
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
Width int `json:"width"`
Height int `json:"height"`
Animated bool `json:"animated"`
Nsfw bool `json:"nsfw"`
}
func (i *ImgurInfoApi) Handler(link string) (*LinkInfo, error) {
if i.opts.ClientId == "" {
return nil, errors.New("invalid client id")
}
u, err := url.Parse(link)
if err != nil {
return nil, err
}
id := filepath.Base(u.Path)
extension := filepath.Ext(id)
if extension != "" {
id = id[0 : len(id)-len(extension)]
}
imageType := "image"
if imgurAlbumRegexp.MatchString(link) {
imageType = "album"
}
req, err := http.NewRequest("GET", fmt.Sprintf(imgurApiUrl, imageType, id), nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Client-ID "+i.opts.ClientId)
res, err := i.api.Client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var response imgurResponse
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, err
}
if !response.Success {
return nil, errors.New("imgur api returned success=false")
}
var title string
if response.Data.Title != "" {
title = "Imgur - " + response.Data.Title
} else {
title = "Imgur - " + id
switch response.Data.Type {
case "image/jpeg":
title += ".jpg"
case "image/png":
title += ".png"
case "image/gif":
title += ".gif"
}
}
attribs := make([]string, 0)
if imageType == "album" {
attribs = append(attribs, "Album")
} else {
attribs = append(attribs, fmt.Sprintf("%dx%d", response.Data.Width, response.Data.Height))
if response.Data.Animated {
attribs = append(attribs, "Animated")
}
}
title += " (" + strings.Join(attribs, ", ") + ")"
if response.Data.Nsfw {
attribs = append(attribs, "NSFW")
}
info := &LinkInfo{
Title: title,
Description: response.Data.Description,
}
return info, nil
} }

22
imgur_test.go Normal file
View File

@ -0,0 +1,22 @@
package linkinfo
import (
"os"
"testing"
)
func TestImgurInfoApi_Handler(t *testing.T) {
api := New(&ImgurOptions{
ClientId: os.Getenv("IMGUR_CLIENT_ID"),
})
info, err := api.Imgur.Handler("https://i.imgur.com/1GKJg79.gifv")
if err != nil {
t.Fatal("Error getting imgur info:", err)
}
if info.Title != "Imgur - Approaching the weekend like (720x404, Animated)" {
t.Fatal("Unexpected title", info.Title)
}
}

View File

@ -12,7 +12,9 @@ type LinkHandler struct {
} }
type LinkInfoApi struct { type LinkInfoApi struct {
Client *http.Client Client *http.Client
Imgur *ImgurInfoApi
linkHandlers []*LinkHandler linkHandlers []*LinkHandler
} }
@ -25,26 +27,45 @@ type LinkInfo struct {
Redirects []string `json:"redirects,omitempty"` Redirects []string `json:"redirects,omitempty"`
} }
func New() *LinkInfoApi { func New(opts ...Option) *LinkInfoApi {
api := &LinkInfoApi{ api := &LinkInfoApi{
Client: &http.Client{ Client: &http.Client{
Timeout: 60 * time.Second, Timeout: 60 * time.Second,
}, },
} }
for _, opt := range opts {
switch opt.(type) {
case *ImgurOptions:
api.Imgur = &ImgurInfoApi{api, opt.(*ImgurOptions)}
}
}
api.registerDefaultHandlers() api.registerDefaultHandlers()
return api return api
} }
func (i *LinkInfoApi) registerDefaultHandlers() { func (i *LinkInfoApi) registerDefaultHandlers() {
i.linkHandlers = make([]*LinkHandler, 0)
if i.Imgur != nil {
i.linkHandlers = append(i.linkHandlers, &LinkHandler{
Hosts: imgurHosts,
Handler: i.Imgur.Handler,
})
}
i.linkHandlers = []*LinkHandler{ i.linkHandlers = []*LinkHandler{
{Hosts: youtubeHosts, Handler: i.YoutubeLinkHandler}, {Hosts: youtubeHosts, Handler: i.YoutubeLinkHandler},
{Hosts: imgurHosts, Handler: i.ImgurLinkHandler},
{Hosts: twitterHosts, Handler: i.TwitterLinkHandler}, {Hosts: twitterHosts, Handler: i.TwitterLinkHandler},
} }
} }
func (i *LinkInfoApi) AddHandler(handler *LinkHandler) {
i.linkHandlers = append(i.linkHandlers, handler)
}
func (i *LinkInfoApi) Retrieve(link string) (*LinkInfo, error) { func (i *LinkInfoApi) Retrieve(link string) (*LinkInfo, error) {
u, err := url.Parse(link) u, err := url.Parse(link)

4
options.go Normal file
View File

@ -0,0 +1,4 @@
package linkinfo
type Option interface {
}