mirror of https://github.com/miniflux/v2.git
Use image included in feed as feed icon
This commit is contained in:
parent
228bb62df4
commit
1aeb1b20da
|
@ -52,6 +52,7 @@ type Feed struct {
|
||||||
FetchViaProxy bool `json:"fetch_via_proxy"`
|
FetchViaProxy bool `json:"fetch_via_proxy"`
|
||||||
Category *Category `json:"category,omitempty"`
|
Category *Category `json:"category,omitempty"`
|
||||||
Entries Entries `json:"entries,omitempty"`
|
Entries Entries `json:"entries,omitempty"`
|
||||||
|
IconURL string `json:"icon_url"`
|
||||||
Icon *FeedIcon `json:"icon"`
|
Icon *FeedIcon `json:"icon"`
|
||||||
HideGlobally bool `json:"hide_globally"`
|
HideGlobally bool `json:"hide_globally"`
|
||||||
UnreadCount int `json:"-"`
|
UnreadCount int `json:"-"`
|
||||||
|
|
|
@ -28,6 +28,7 @@ type atom10Feed struct {
|
||||||
ID string `xml:"id"`
|
ID string `xml:"id"`
|
||||||
Title atom10Text `xml:"title"`
|
Title atom10Text `xml:"title"`
|
||||||
Authors atomAuthors `xml:"author"`
|
Authors atomAuthors `xml:"author"`
|
||||||
|
Icon string `xml:"icon"`
|
||||||
Links atomLinks `xml:"link"`
|
Links atomLinks `xml:"link"`
|
||||||
Entries []atom10Entry `xml:"entry"`
|
Entries []atom10Entry `xml:"entry"`
|
||||||
}
|
}
|
||||||
|
@ -54,6 +55,8 @@ func (a *atom10Feed) Transform(baseURL string) *model.Feed {
|
||||||
feed.Title = feed.SiteURL
|
feed.Title = feed.SiteURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
feed.IconURL = strings.TrimSpace(a.Icon)
|
||||||
|
|
||||||
for _, entry := range a.Entries {
|
for _, entry := range a.Entries {
|
||||||
item := entry.Transform()
|
item := entry.Transform()
|
||||||
entryURL, err := url.AbsoluteURL(feed.SiteURL, item.URL)
|
entryURL, err := url.AbsoluteURL(feed.SiteURL, item.URL)
|
||||||
|
|
|
@ -96,6 +96,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
|
||||||
store,
|
store,
|
||||||
subscription.ID,
|
subscription.ID,
|
||||||
subscription.SiteURL,
|
subscription.SiteURL,
|
||||||
|
subscription.IconURL,
|
||||||
feedCreationRequest.UserAgent,
|
feedCreationRequest.UserAgent,
|
||||||
feedCreationRequest.FetchViaProxy,
|
feedCreationRequest.FetchViaProxy,
|
||||||
feedCreationRequest.AllowSelfSignedCertificates,
|
feedCreationRequest.AllowSelfSignedCertificates,
|
||||||
|
@ -189,6 +190,7 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64) error {
|
||||||
store,
|
store,
|
||||||
originalFeed.ID,
|
originalFeed.ID,
|
||||||
originalFeed.SiteURL,
|
originalFeed.SiteURL,
|
||||||
|
updatedFeed.IconURL,
|
||||||
originalFeed.UserAgent,
|
originalFeed.UserAgent,
|
||||||
originalFeed.FetchViaProxy,
|
originalFeed.FetchViaProxy,
|
||||||
originalFeed.AllowSelfSignedCertificates,
|
originalFeed.AllowSelfSignedCertificates,
|
||||||
|
@ -208,9 +210,9 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64) error {
|
||||||
return nil
|
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) {
|
if !store.HasIcon(feedID) {
|
||||||
icon, err := icon.FindIcon(websiteURL, userAgent, fetchViaProxy, allowSelfSignedCertificates)
|
icon, err := icon.FindIcon(websiteURL, iconURL, userAgent, fetchViaProxy, allowSelfSignedCertificates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug(`[CheckFeedIcon] %v (feedID=%d websiteURL=%s)`, err, feedID, websiteURL)
|
logger.Debug(`[CheckFeedIcon] %v (feedID=%d websiteURL=%s)`, err, feedID, websiteURL)
|
||||||
} else if icon == nil {
|
} else if icon == nil {
|
||||||
|
|
|
@ -23,30 +23,32 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindIcon try to find the website's icon.
|
// FindIcon try to find the website's icon.
|
||||||
func FindIcon(websiteURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) (*model.Icon, error) {
|
func FindIcon(websiteURL, iconURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) (*model.Icon, error) {
|
||||||
rootURL := url.RootURL(websiteURL)
|
if iconURL == "" {
|
||||||
logger.Debug("[FindIcon] Trying to find an icon: rootURL=%q websiteURL=%q userAgent=%q", rootURL, websiteURL, userAgent)
|
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 := client.NewClientWithConfig(rootURL, config.Opts)
|
||||||
clt.WithUserAgent(userAgent)
|
clt.WithUserAgent(userAgent)
|
||||||
clt.AllowSelfSignedCertificates = allowSelfSignedCertificates
|
clt.AllowSelfSignedCertificates = allowSelfSignedCertificates
|
||||||
|
|
||||||
if fetchViaProxy {
|
if fetchViaProxy {
|
||||||
clt.WithProxy()
|
clt.WithProxy()
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := clt.Get()
|
response, err := clt.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("icon: unable to download website index page: %v", err)
|
return nil, fmt.Errorf("icon: unable to download website index page: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.HasServerFailure() {
|
if response.HasServerFailure() {
|
||||||
return nil, fmt.Errorf("icon: unable to download website index page: status=%d", response.StatusCode)
|
return nil, fmt.Errorf("icon: unable to download website index page: status=%d", response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
iconURL, err := parseDocument(rootURL, response.Body)
|
iconURL, err = parseDocument(rootURL, response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(iconURL, "data:") {
|
if strings.HasPrefix(iconURL, "data:") {
|
||||||
|
|
|
@ -17,13 +17,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type jsonFeed struct {
|
type jsonFeed struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
SiteURL string `json:"home_page_url"`
|
SiteURL string `json:"home_page_url"`
|
||||||
FeedURL string `json:"feed_url"`
|
IconURL string `json:"icon"`
|
||||||
Authors []jsonAuthor `json:"authors"`
|
FaviconURL string `json:"favicon"`
|
||||||
Author jsonAuthor `json:"author"`
|
FeedURL string `json:"feed_url"`
|
||||||
Items []jsonItem `json:"items"`
|
Authors []jsonAuthor `json:"authors"`
|
||||||
|
Author jsonAuthor `json:"author"`
|
||||||
|
Items []jsonItem `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonAuthor struct {
|
type jsonAuthor struct {
|
||||||
|
@ -76,6 +78,12 @@ func (j *jsonFeed) Transform(baseURL string) *model.Feed {
|
||||||
feed.SiteURL = j.SiteURL
|
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)
|
feed.Title = strings.TrimSpace(j.Title)
|
||||||
if feed.Title == "" {
|
if feed.Title == "" {
|
||||||
feed.Title = feed.SiteURL
|
feed.Title = feed.SiteURL
|
||||||
|
|
|
@ -15,6 +15,8 @@ func TestParseJsonFeed(t *testing.T) {
|
||||||
data := `{
|
data := `{
|
||||||
"version": "https://jsonfeed.org/version/1",
|
"version": "https://jsonfeed.org/version/1",
|
||||||
"title": "My Example Feed",
|
"title": "My Example Feed",
|
||||||
|
"icon": "https://micro.blog/jsonfeed/avatar.jpg",
|
||||||
|
"favicon": "https://micro.blog/jsonfeed/favicon.png",
|
||||||
"home_page_url": "https://example.org/",
|
"home_page_url": "https://example.org/",
|
||||||
"feed_url": "https://example.org/feed.json",
|
"feed_url": "https://example.org/feed.json",
|
||||||
"items": [
|
"items": [
|
||||||
|
@ -48,6 +50,10 @@ func TestParseJsonFeed(t *testing.T) {
|
||||||
t.Errorf("Incorrect site URL, got: %s", feed.SiteURL)
|
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 {
|
if len(feed.Entries) != 2 {
|
||||||
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
|
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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@ func TestParseRss2Sample(t *testing.T) {
|
||||||
<title>Liftoff News</title>
|
<title>Liftoff News</title>
|
||||||
<link>http://liftoff.msfc.nasa.gov/</link>
|
<link>http://liftoff.msfc.nasa.gov/</link>
|
||||||
<description>Liftoff to Space Exploration.</description>
|
<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>
|
<language>en-us</language>
|
||||||
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
|
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
|
||||||
<lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
|
<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)
|
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 {
|
if len(feed.Entries) != 4 {
|
||||||
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
|
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ type rssFeed struct {
|
||||||
Version string `xml:"version,attr"`
|
Version string `xml:"version,attr"`
|
||||||
Title string `xml:"channel>title"`
|
Title string `xml:"channel>title"`
|
||||||
Links []rssLink `xml:"channel>link"`
|
Links []rssLink `xml:"channel>link"`
|
||||||
|
ImageURL string `xml:"channel>image>url"`
|
||||||
Language string `xml:"channel>language"`
|
Language string `xml:"channel>language"`
|
||||||
Description string `xml:"channel>description"`
|
Description string `xml:"channel>description"`
|
||||||
PubDate string `xml:"channel>pubDate"`
|
PubDate string `xml:"channel>pubDate"`
|
||||||
|
@ -58,6 +59,8 @@ func (r *rssFeed) Transform(baseURL string) *model.Feed {
|
||||||
feed.Title = feed.SiteURL
|
feed.Title = feed.SiteURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
feed.IconURL = strings.TrimSpace(r.ImageURL)
|
||||||
|
|
||||||
for _, item := range r.Items {
|
for _, item := range r.Items {
|
||||||
entry := item.Transform()
|
entry := item.Transform()
|
||||||
if entry.Author == "" {
|
if entry.Author == "" {
|
||||||
|
|
Loading…
Reference in New Issue