Use image included in feed as feed icon

This commit is contained in:
Ryan Stafford 2023-06-04 18:01:59 -04:00 committed by GitHub
parent 228bb62df4
commit 1aeb1b20da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 92 additions and 28 deletions

View File

@ -52,6 +52,7 @@ type Feed struct {
FetchViaProxy bool `json:"fetch_via_proxy"`
Category *Category `json:"category,omitempty"`
Entries Entries `json:"entries,omitempty"`
IconURL string `json:"icon_url"`
Icon *FeedIcon `json:"icon"`
HideGlobally bool `json:"hide_globally"`
UnreadCount int `json:"-"`

View File

@ -28,6 +28,7 @@ type atom10Feed struct {
ID string `xml:"id"`
Title atom10Text `xml:"title"`
Authors atomAuthors `xml:"author"`
Icon string `xml:"icon"`
Links atomLinks `xml:"link"`
Entries []atom10Entry `xml:"entry"`
}
@ -54,6 +55,8 @@ func (a *atom10Feed) Transform(baseURL string) *model.Feed {
feed.Title = feed.SiteURL
}
feed.IconURL = strings.TrimSpace(a.Icon)
for _, entry := range a.Entries {
item := entry.Transform()
entryURL, err := url.AbsoluteURL(feed.SiteURL, item.URL)

View File

@ -96,6 +96,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
store,
subscription.ID,
subscription.SiteURL,
subscription.IconURL,
feedCreationRequest.UserAgent,
feedCreationRequest.FetchViaProxy,
feedCreationRequest.AllowSelfSignedCertificates,
@ -189,6 +190,7 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64) error {
store,
originalFeed.ID,
originalFeed.SiteURL,
updatedFeed.IconURL,
originalFeed.UserAgent,
originalFeed.FetchViaProxy,
originalFeed.AllowSelfSignedCertificates,
@ -208,9 +210,9 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64) error {
return nil
}
func checkFeedIcon(store *storage.Storage, feedID int64, websiteURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) {
func checkFeedIcon(store *storage.Storage, feedID int64, websiteURL, iconURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) {
if !store.HasIcon(feedID) {
icon, err := icon.FindIcon(websiteURL, userAgent, fetchViaProxy, allowSelfSignedCertificates)
icon, err := icon.FindIcon(websiteURL, iconURL, userAgent, fetchViaProxy, allowSelfSignedCertificates)
if err != nil {
logger.Debug(`[CheckFeedIcon] %v (feedID=%d websiteURL=%s)`, err, feedID, websiteURL)
} else if icon == nil {

View File

@ -23,30 +23,32 @@ import (
)
// FindIcon try to find the website's icon.
func FindIcon(websiteURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) (*model.Icon, error) {
rootURL := url.RootURL(websiteURL)
logger.Debug("[FindIcon] Trying to find an icon: rootURL=%q websiteURL=%q userAgent=%q", rootURL, websiteURL, userAgent)
func FindIcon(websiteURL, iconURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) (*model.Icon, error) {
if iconURL == "" {
rootURL := url.RootURL(websiteURL)
logger.Debug("[FindIcon] Trying to find an icon: rootURL=%q websiteURL=%q userAgent=%q", rootURL, websiteURL, userAgent)
clt := client.NewClientWithConfig(rootURL, config.Opts)
clt.WithUserAgent(userAgent)
clt.AllowSelfSignedCertificates = allowSelfSignedCertificates
clt := client.NewClientWithConfig(rootURL, config.Opts)
clt.WithUserAgent(userAgent)
clt.AllowSelfSignedCertificates = allowSelfSignedCertificates
if fetchViaProxy {
clt.WithProxy()
}
if fetchViaProxy {
clt.WithProxy()
}
response, err := clt.Get()
if err != nil {
return nil, fmt.Errorf("icon: unable to download website index page: %v", err)
}
response, err := clt.Get()
if err != nil {
return nil, fmt.Errorf("icon: unable to download website index page: %v", err)
}
if response.HasServerFailure() {
return nil, fmt.Errorf("icon: unable to download website index page: status=%d", response.StatusCode)
}
if response.HasServerFailure() {
return nil, fmt.Errorf("icon: unable to download website index page: status=%d", response.StatusCode)
}
iconURL, err := parseDocument(rootURL, response.Body)
if err != nil {
return nil, err
iconURL, err = parseDocument(rootURL, response.Body)
if err != nil {
return nil, err
}
}
if strings.HasPrefix(iconURL, "data:") {

View File

@ -17,13 +17,15 @@ import (
)
type jsonFeed struct {
Version string `json:"version"`
Title string `json:"title"`
SiteURL string `json:"home_page_url"`
FeedURL string `json:"feed_url"`
Authors []jsonAuthor `json:"authors"`
Author jsonAuthor `json:"author"`
Items []jsonItem `json:"items"`
Version string `json:"version"`
Title string `json:"title"`
SiteURL string `json:"home_page_url"`
IconURL string `json:"icon"`
FaviconURL string `json:"favicon"`
FeedURL string `json:"feed_url"`
Authors []jsonAuthor `json:"authors"`
Author jsonAuthor `json:"author"`
Items []jsonItem `json:"items"`
}
type jsonAuthor struct {
@ -76,6 +78,12 @@ func (j *jsonFeed) Transform(baseURL string) *model.Feed {
feed.SiteURL = j.SiteURL
}
feed.IconURL = strings.TrimSpace(j.IconURL)
if feed.IconURL == "" {
feed.IconURL = strings.TrimSpace(j.FaviconURL)
}
feed.Title = strings.TrimSpace(j.Title)
if feed.Title == "" {
feed.Title = feed.SiteURL

View File

@ -15,6 +15,8 @@ func TestParseJsonFeed(t *testing.T) {
data := `{
"version": "https://jsonfeed.org/version/1",
"title": "My Example Feed",
"icon": "https://micro.blog/jsonfeed/avatar.jpg",
"favicon": "https://micro.blog/jsonfeed/favicon.png",
"home_page_url": "https://example.org/",
"feed_url": "https://example.org/feed.json",
"items": [
@ -48,6 +50,10 @@ func TestParseJsonFeed(t *testing.T) {
t.Errorf("Incorrect site URL, got: %s", feed.SiteURL)
}
if feed.IconURL != "https://micro.blog/jsonfeed/avatar.jpg" {
t.Errorf("Incorrect icon URL, got: %s", feed.IconURL)
}
if len(feed.Entries) != 2 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
@ -617,3 +623,33 @@ func TestParseTags(t *testing.T) {
t.Errorf("Incorrect entry tag, got %q instead of %q", result, expected)
}
}
func TestParseFavicon(t *testing.T) {
data := `{
"version": "https://jsonfeed.org/version/1",
"title": "My Example Feed",
"favicon": "https://micro.blog/jsonfeed/favicon.png",
"home_page_url": "https://example.org/",
"feed_url": "https://example.org/feed.json",
"items": [
{
"id": "2",
"content_text": "This is a second item.",
"url": "https://example.org/second-item"
},
{
"id": "1",
"content_html": "<p>Hello, world!</p>",
"url": "https://example.org/initial-post"
}
]
}`
feed, err := Parse("https://example.org/feed.json", bytes.NewBufferString(data))
if err != nil {
t.Fatal(err)
}
if feed.IconURL != "https://micro.blog/jsonfeed/favicon.png" {
t.Errorf("Incorrect icon URL, got: %s", feed.IconURL)
}
}

View File

@ -18,6 +18,11 @@ func TestParseRss2Sample(t *testing.T) {
<title>Liftoff News</title>
<link>http://liftoff.msfc.nasa.gov/</link>
<description>Liftoff to Space Exploration.</description>
<image>
<url>http://liftoff.msfc.nasa.gov/HomePageXtra/MeatBall.gif</url>
<title>NASA</title>
<link>http://liftoff.msfc.nasa.gov/</link>
</image>
<language>en-us</language>
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
<lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
@ -71,6 +76,10 @@ func TestParseRss2Sample(t *testing.T) {
t.Errorf("Incorrect site URL, got: %s", feed.SiteURL)
}
if feed.IconURL != "http://liftoff.msfc.nasa.gov/HomePageXtra/MeatBall.gif" {
t.Errorf("Incorrect image URL, got: %s", feed.IconURL)
}
if len(feed.Entries) != 4 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}

View File

@ -27,6 +27,7 @@ type rssFeed struct {
Version string `xml:"version,attr"`
Title string `xml:"channel>title"`
Links []rssLink `xml:"channel>link"`
ImageURL string `xml:"channel>image>url"`
Language string `xml:"channel>language"`
Description string `xml:"channel>description"`
PubDate string `xml:"channel>pubDate"`
@ -58,6 +59,8 @@ func (r *rssFeed) Transform(baseURL string) *model.Feed {
feed.Title = feed.SiteURL
}
feed.IconURL = strings.TrimSpace(r.ImageURL)
for _, item := range r.Items {
entry := item.Transform()
if entry.Author == "" {