Improve getFaviconFileBuffer()

This commit is contained in:
Yang Luo 2023-06-17 12:50:01 +08:00
parent b9140e2d5a
commit ac4b870309
3 changed files with 206 additions and 17 deletions

View File

@ -54,19 +54,24 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
// Get the content type and determine the file extension // Get the content type and determine the file extension
contentType := resp.Header.Get("Content-Type") contentType := resp.Header.Get("Content-Type")
fileExtension := "" fileExtension := ""
switch contentType {
case "image/jpeg": if strings.Contains(contentType, "text/html") {
fileExtension = ".jpg" fileExtension = ".html"
case "image/png": } else {
fileExtension = ".png" switch contentType {
case "image/gif": case "image/jpeg":
fileExtension = ".gif" fileExtension = ".jpg"
case "image/vnd.microsoft.icon": case "image/png":
fileExtension = ".ico" fileExtension = ".png"
case "image/x-icon": case "image/gif":
fileExtension = ".ico" fileExtension = ".gif"
default: case "image/vnd.microsoft.icon":
return nil, "", fmt.Errorf("unsupported content type: %s", contentType) fileExtension = ".ico"
case "image/x-icon":
fileExtension = ".ico"
default:
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
}
} }
// Save the image to a bytes.Buffer // Save the image to a bytes.Buffer

View File

@ -18,9 +18,173 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"strings" "strings"
"golang.org/x/net/html"
) )
type Link struct {
Rel string
Sizes string
Href string
}
func GetFaviconUrl(htmlStr string) (string, error) {
doc, err := html.Parse(strings.NewReader(htmlStr))
if err != nil {
return "", err
}
var links []Link
findLinks(doc, &links)
if len(links) == 0 {
return "", fmt.Errorf("no Favicon links found")
}
chosenLink := chooseFaviconLink(links)
if chosenLink == nil {
return "", fmt.Errorf("unable to determine favicon URL")
}
return chosenLink.Href, nil
}
func findLinks(n *html.Node, links *[]Link) {
if n.Type == html.ElementNode && n.Data == "link" {
link := parseLink(n)
if link != nil {
*links = append(*links, *link)
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
findLinks(c, links)
}
}
func parseLink(n *html.Node) *Link {
var link Link
for _, attr := range n.Attr {
switch attr.Key {
case "rel":
link.Rel = attr.Val
case "sizes":
link.Sizes = attr.Val
case "href":
link.Href = attr.Val
}
}
if link.Href != "" {
return &link
}
return nil
}
func chooseFaviconLink(links []Link) *Link {
var appleTouchLinks []Link
var shortcutLinks []Link
var iconLinks []Link
for _, link := range links {
switch link.Rel {
case "apple-touch-icon":
appleTouchLinks = append(appleTouchLinks, link)
case "shortcut icon":
shortcutLinks = append(shortcutLinks, link)
case "icon":
iconLinks = append(iconLinks, link)
}
}
if len(appleTouchLinks) > 0 {
return chooseFaviconLinkBySizes(appleTouchLinks)
}
if len(shortcutLinks) > 0 {
return chooseFaviconLinkBySizes(shortcutLinks)
}
if len(iconLinks) > 0 {
return chooseFaviconLinkBySizes(iconLinks)
}
return nil
}
func chooseFaviconLinkBySizes(links []Link) *Link {
if len(links) == 1 {
return &links[0]
}
var chosenLink *Link
for _, link := range links {
if chosenLink == nil || compareSizes(link.Sizes, chosenLink.Sizes) > 0 {
chosenLink = &link
}
}
return chosenLink
}
func compareSizes(sizes1, sizes2 string) int {
if sizes1 == sizes2 {
return 0
}
size1 := parseSize(sizes1)
size2 := parseSize(sizes2)
if size1 == nil {
return -1
}
if size2 == nil {
return 1
}
if size1[0] == size2[0] {
return size1[1] - size2[1]
}
return size1[0] - size2[0]
}
func parseSize(sizes string) []int {
size := strings.Split(sizes, "x")
if len(size) != 2 {
return nil
}
var result []int
for _, s := range size {
val := strings.TrimSpace(s)
if len(val) > 0 {
num := 0
for i := 0; i < len(val); i++ {
if val[i] >= '0' && val[i] <= '9' {
num = num*10 + int(val[i]-'0')
} else {
break
}
}
result = append(result, num)
}
}
if len(result) == 2 {
return result
}
return nil
}
func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) { func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
tokens := strings.Split(email, "@") tokens := strings.Split(email, "@")
domain := tokens[1] domain := tokens[1]
@ -28,9 +192,29 @@ func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, str
return nil, "", nil return nil, "", nil
} }
//htmlUrl := fmt.Sprintf("https://%s", domain) htmlUrl := fmt.Sprintf("https://%s", domain)
//buffer, fileExtension, err := downloadImage(client, htmlUrl) buffer, _, err := downloadImage(client, htmlUrl)
if err != nil {
return nil, "", err
}
faviconUrl := fmt.Sprintf("https://%s/favicon.ico", domain) faviconUrl := ""
if buffer != nil {
faviconUrl, err = GetFaviconUrl(buffer.String())
if err != nil {
return nil, "", err
}
if !strings.HasPrefix(faviconUrl, "http") {
faviconUrl, err = url.JoinPath(htmlUrl, faviconUrl)
if err != nil {
return nil, "", err
}
}
}
if faviconUrl == "" {
faviconUrl = fmt.Sprintf("https://%s/favicon.ico", domain)
}
return downloadImage(client, faviconUrl) return downloadImage(client, faviconUrl)
} }

View File

@ -61,7 +61,7 @@ func TestUpdateAvatars(t *testing.T) {
// continue // continue
//} //}
if user.AvatarType != "Favicon" { if user.AvatarType != "Auto" {
continue continue
} }