Skip to content

Commit

Permalink
Add support for refreshing feed icons
Browse files Browse the repository at this point in the history
  • Loading branch information
rystaf committed Oct 9, 2023
1 parent 52cf236 commit 55734f2
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 34 deletions.
9 changes: 9 additions & 0 deletions internal/database/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,4 +783,13 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE public.feed_icons DROP CONSTRAINT feed_icons_pkey;
ALTER TABLE public.feed_icons ADD CONSTRAINT feed_icons_pkey PRIMARY KEY (feed_id);
ALTER TABLE feeds ADD COLUMN refresh_icon bool default 'f';
`
_, err = tx.Exec(sql)
return err
},
}
1 change: 1 addition & 0 deletions internal/locale/translations/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@
"form.feed.label.disabled": "Do not refresh this feed",
"form.feed.label.no_media_player": "No media player (audio/video)",
"form.feed.label.hide_globally": "Hide entries in global unread list",
"form.feed.label.refresh_icon": "Refresh icon",
"form.feed.fieldset.general": "General",
"form.feed.fieldset.rules": "Rules",
"form.feed.fieldset.network_settings": "Network Settings",
Expand Down
1 change: 1 addition & 0 deletions internal/model/feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Feed struct {
UnreadCount int `json:"-"`
ReadCount int `json:"-"`
AppriseServiceURLs string `json:"apprise_service_urls"`
RefreshIcon bool `json:"refresh_icon"`
}

type FeedCounters struct {
Expand Down
56 changes: 28 additions & 28 deletions internal/reader/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,17 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64, forceRefresh bool
// because some websites don't return the same headers when replying with a 304.
originalFeed.WithClientResponse(response)

checkFeedIcon(
store,
originalFeed.ID,
originalFeed.SiteURL,
updatedFeed.IconURL,
originalFeed.UserAgent,
originalFeed.FetchViaProxy,
originalFeed.AllowSelfSignedCertificates,
)
if !store.HasIcon(originalFeed.ID) || originalFeed.RefreshIcon || forceRefresh {
checkFeedIcon(
store,
originalFeed.ID,
originalFeed.SiteURL,
updatedFeed.IconURL,
originalFeed.UserAgent,
originalFeed.FetchViaProxy,
originalFeed.AllowSelfSignedCertificates,
)
}
} else {
slog.Debug("Feed not modified",
slog.Int64("user_id", userID),
Expand All @@ -240,30 +242,28 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64, forceRefresh bool
}

func checkFeedIcon(store *storage.Storage, feedID int64, websiteURL, feedIconURL, userAgent string, fetchViaProxy, allowSelfSignedCertificates bool) {
if !store.HasIcon(feedID) {
icon, err := icon.FindIcon(websiteURL, feedIconURL, userAgent, fetchViaProxy, allowSelfSignedCertificates)
if err != nil {
slog.Warn("Unable to find feed icon",
icon, err := icon.FindIcon(websiteURL, feedIconURL, userAgent, fetchViaProxy, allowSelfSignedCertificates)
if err != nil {
slog.Warn("Unable to find feed icon",
slog.Int64("feed_id", feedID),
slog.String("website_url", websiteURL),
slog.String("feed_icon_url", feedIconURL),
slog.Any("error", err),
)
} else if icon == nil {
slog.Debug("No icon found",
slog.Int64("feed_id", feedID),
slog.String("website_url", websiteURL),
slog.String("feed_icon_url", feedIconURL),
)
} else if !store.HasIconHash(feedID, icon.Hash) {
if err := store.CreateFeedIcon(feedID, icon); err != nil {
slog.Error("Unable to store feed icon",
slog.Int64("feed_id", feedID),
slog.String("website_url", websiteURL),
slog.String("feed_icon_url", feedIconURL),
slog.Any("error", err),
)
} else if icon == nil {
slog.Debug("No icon found",
slog.Int64("feed_id", feedID),
slog.String("website_url", websiteURL),
slog.String("feed_icon_url", feedIconURL),
)
} else {
if err := store.CreateFeedIcon(feedID, icon); err != nil {
slog.Error("Unable to store feed icon",
slog.Int64("feed_id", feedID),
slog.String("website_url", websiteURL),
slog.String("feed_icon_url", feedIconURL),
slog.Any("error", err),
)
}
}
}
}
12 changes: 8 additions & 4 deletions internal/storage/feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,11 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
hide_globally,
url_rewrite_rules,
no_media_player,
apprise_service_urls
apprise_service_urls,
refresh_icon
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)
RETURNING
id
`
Expand Down Expand Up @@ -273,6 +274,7 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
feed.UrlRewriteRules,
feed.NoMediaPlayer,
feed.AppriseServiceURLs,
feed.RefreshIcon,
).Scan(&feed.ID)
if err != nil {
return fmt.Errorf(`store: unable to create feed %q: %v`, feed.FeedURL, err)
Expand Down Expand Up @@ -344,9 +346,10 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
hide_globally=$24,
url_rewrite_rules=$25,
no_media_player=$26,
apprise_service_urls=$27
apprise_service_urls=$27,
refresh_icon=$28
WHERE
id=$28 AND user_id=$29
id=$29 AND user_id=$30
`
_, err = s.db.Exec(query,
feed.FeedURL,
Expand Down Expand Up @@ -376,6 +379,7 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
feed.UrlRewriteRules,
feed.NoMediaPlayer,
feed.AppriseServiceURLs,
feed.RefreshIcon,
feed.ID,
feed.UserID,
)
Expand Down
4 changes: 3 additions & 1 deletion internal/storage/feed_query_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) {
c.hide_globally as category_hidden,
fi.icon_id,
u.timezone,
f.apprise_service_urls
f.apprise_service_urls,
f.refresh_icon
FROM
feeds f
LEFT JOIN
Expand Down Expand Up @@ -228,6 +229,7 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) {
&iconID,
&tz,
&feed.AppriseServiceURLs,
&feed.RefreshIcon,
)

if err != nil {
Expand Down
12 changes: 11 additions & 1 deletion internal/storage/icon.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ func (s *Storage) HasIcon(feedID int64) bool {
return result
}

// HasIconHash checks if given feed has icon hash
func (s *Storage) HasIconHash(feedID int64, iconHash string) bool {
var result bool
query := `SELECT true FROM feed_icons
LEFT JOIN icons ON feed_icons.icon_id=icons.id
WHERE feed_id=$1 AND icons.hash=$2`
s.db.QueryRow(query, feedID).Scan(&result)
return result
}

// IconByID returns an icon by the ID.
func (s *Storage) IconByID(iconID int64) (*model.Icon, error) {
var icon model.Icon
Expand Down Expand Up @@ -107,7 +117,7 @@ func (s *Storage) CreateFeedIcon(feedID int64, icon *model.Icon) error {
}
}

_, err = s.db.Exec(`INSERT INTO feed_icons (feed_id, icon_id) VALUES ($1, $2)`, feedID, icon.ID)
_, err = s.db.Exec(`INSERT INTO feed_icons (feed_id, icon_id) VALUES ($1, $2) ON CONFLICT (feed_id) DO UPDATE SET icon_id = $2`, feedID, icon.ID)
if err != nil {
return fmt.Errorf(`store: unable to create feed icon: %v`, err)
}
Expand Down
1 change: 1 addition & 0 deletions internal/template/templates/views/edit_feed.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ <h3>{{ t "page.edit_feed.last_parsing_error" }}</h3>
<label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
<label><input type="checkbox" name="ignore_http_cache" value="1" {{ if .form.IgnoreHTTPCache }}checked{{ end }}> {{ t "form.feed.label.ignore_http_cache" }}</label>
<label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label>
<label><input type="checkbox" name="refresh_icon" value="1" {{ if .form.RefreshIcon }}checked{{ end }}> {{ t "form.feed.label.refresh_icon" }}</label>
{{ if .hasProxyConfigured }}
<label><input type="checkbox" name="fetch_via_proxy" value="1" {{ if .form.FetchViaProxy }}checked{{ end }}> {{ t "form.feed.label.fetch_via_proxy" }}</label>
{{ end }}
Expand Down
1 change: 1 addition & 0 deletions internal/ui/feed_edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) {
HideGlobally: feed.HideGlobally,
CategoryHidden: feed.Category.HideGlobally,
AppriseServiceURLs: feed.AppriseServiceURLs,
RefreshIcon: feed.RefreshIcon,
}

sess := session.New(h.store, request.SessionID(r))
Expand Down
3 changes: 3 additions & 0 deletions internal/ui/form/feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type FeedForm struct {
HideGlobally bool
CategoryHidden bool // Category has "hide_globally"
AppriseServiceURLs string
RefreshIcon bool
}

// Merge updates the fields of the given feed.
Expand Down Expand Up @@ -61,6 +62,7 @@ func (f FeedForm) Merge(feed *model.Feed) *model.Feed {
feed.NoMediaPlayer = f.NoMediaPlayer
feed.HideGlobally = f.HideGlobally
feed.AppriseServiceURLs = f.AppriseServiceURLs
feed.RefreshIcon = f.RefreshIcon
return feed
}

Expand Down Expand Up @@ -92,5 +94,6 @@ func NewFeedForm(r *http.Request) *FeedForm {
NoMediaPlayer: r.FormValue("no_media_player") == "1",
HideGlobally: r.FormValue("hide_globally") == "1",
AppriseServiceURLs: r.FormValue("apprise_service_urls"),
RefreshIcon: r.FormValue("refresh_icon") == "1",
}
}

0 comments on commit 55734f2

Please sign in to comment.