Skip to content

Commit

Permalink
Take RSS TTL field into consideration to schedule next check date
Browse files Browse the repository at this point in the history
  • Loading branch information
fguillot committed Oct 21, 2023
1 parent 4cc9988 commit 341e7d4
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 11 deletions.
16 changes: 10 additions & 6 deletions internal/model/feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ type Feed struct {
IgnoreHTTPCache bool `json:"ignore_http_cache"`
AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"`
FetchViaProxy bool `json:"fetch_via_proxy"`
Category *Category `json:"category,omitempty"`
Entries Entries `json:"entries,omitempty"`
IconURL string `json:"-"`
Icon *FeedIcon `json:"icon"`
HideGlobally bool `json:"hide_globally"`
UnreadCount int `json:"-"`
ReadCount int `json:"-"`
AppriseServiceURLs string `json:"apprise_service_urls"`

// Non persisted attributes
Category *Category `json:"category,omitempty"`
Icon *FeedIcon `json:"icon"`
Entries Entries `json:"entries,omitempty"`

TTL int `json:"-"`
IconURL string `json:"-"`
UnreadCount int `json:"-"`
ReadCount int `json:"-"`
}

type FeedCounters struct {
Expand Down
23 changes: 23 additions & 0 deletions internal/reader/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package handler // import "miniflux.app/v2/internal/reader/handler"

import (
"log/slog"
"time"

"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/errors"
Expand Down Expand Up @@ -185,6 +186,28 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64, forceRefresh bool
return parseErr
}

// If the feed has a TTL defined, we use it to make sure we don't check it too often.
if updatedFeed.TTL > 0 {
minNextCheckAt := time.Now().Add(time.Minute * time.Duration(updatedFeed.TTL))
slog.Debug("Feed TTL",
slog.Int64("user_id", userID),
slog.Int64("feed_id", feedID),
slog.Int("ttl", updatedFeed.TTL),
slog.Time("next_check_at", originalFeed.NextCheckAt),
)

if originalFeed.NextCheckAt.IsZero() || originalFeed.NextCheckAt.Before(minNextCheckAt) {
slog.Debug("Updating next check date based on TTL",
slog.Int64("user_id", userID),
slog.Int64("feed_id", feedID),
slog.Int("ttl", updatedFeed.TTL),
slog.Time("new_next_check_at", minNextCheckAt),
slog.Time("old_next_check_at", originalFeed.NextCheckAt),
)
originalFeed.NextCheckAt = minNextCheckAt
}
}

originalFeed.Entries = updatedFeed.Entries
processor.ProcessFeedEntries(store, originalFeed, user, forceRefresh)

Expand Down
48 changes: 48 additions & 0 deletions internal/reader/rss/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1500,3 +1500,51 @@ func TestParseEntryWithCategoryAndCDATA(t *testing.T) {
t.Errorf("Incorrect entry category, got %q instead of %q", result, expected)
}
}

func TestParseFeedWithTTLField(t *testing.T) {
data := `<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Example</title>
<link>https://example.org/</link>
<ttl>60</ttl>
<item>
<title>Test</title>
<link>https://example.org/item</link>
</item>
</channel>
</rss>`

feed, err := Parse("https://example.org/", bytes.NewBufferString(data))
if err != nil {
t.Fatal(err)
}

if feed.TTL != 60 {
t.Errorf("Incorrect TTL, got: %d", feed.TTL)
}
}

func TestParseFeedWithIncorrectTTLValue(t *testing.T) {
data := `<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Example</title>
<link>https://example.org/</link>
<ttl>invalid</ttl>
<item>
<title>Test</title>
<link>https://example.org/item</link>
</item>
</channel>
</rss>`

feed, err := Parse("https://example.org/", bytes.NewBufferString(data))
if err != nil {
t.Fatal(err)
}

if feed.TTL != 0 {
t.Errorf("Incorrect TTL, got: %d", feed.TTL)
}
}
10 changes: 5 additions & 5 deletions internal/reader/rss/podcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
package rss // import "miniflux.app/v2/internal/reader/rss"

import (
"fmt"
"errors"
"math"
"strconv"
"strings"
)

var ErrInvalidDurationFormat = errors.New("rss: invalid duration format")

// PodcastFeedElement represents iTunes and GooglePlay feed XML elements.
// Specs:
// - https://github.com/simplepie/simplepie-ng/wiki/Spec:-iTunes-Podcast-RSS
Expand Down Expand Up @@ -74,21 +76,19 @@ func (e *PodcastEntryElement) PodcastDescription() string {
return strings.TrimSpace(description)
}

var invalidDurationFormatErr = fmt.Errorf("rss: invalid duration format")

// normalizeDuration returns the duration tag value as a number of minutes
func normalizeDuration(rawDuration string) (int, error) {
var sumSeconds int

durationParts := strings.Split(rawDuration, ":")
if len(durationParts) > 3 {
return 0, invalidDurationFormatErr
return 0, ErrInvalidDurationFormat
}

for i, durationPart := range durationParts {
durationPartValue, err := strconv.Atoi(durationPart)
if err != nil {
return 0, invalidDurationFormatErr
return 0, ErrInvalidDurationFormat
}

sumSeconds += int(math.Pow(60, float64(len(durationParts)-i-1))) * durationPartValue
Expand Down
19 changes: 19 additions & 0 deletions internal/reader/rss/rss.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,28 @@ type rssFeed struct {
PubDate string `xml:"channel>pubDate"`
ManagingEditor string `xml:"channel>managingEditor"`
Webmaster string `xml:"channel>webMaster"`
TimeToLive rssTTL `xml:"channel>ttl"`
Items []rssItem `xml:"channel>item"`
PodcastFeedElement
}

type rssTTL struct {
Data string `xml:",chardata"`
}

func (r *rssTTL) Value() int {
if r.Data == "" {
return 0
}

value, err := strconv.Atoi(r.Data)
if err != nil {
return 0
}

return value
}

func (r *rssFeed) Transform(baseURL string) *model.Feed {
var err error

Expand All @@ -60,6 +78,7 @@ func (r *rssFeed) Transform(baseURL string) *model.Feed {
}

feed.IconURL = strings.TrimSpace(r.ImageURL)
feed.TTL = r.TimeToLive.Value()

for _, item := range r.Items {
entry := item.Transform()
Expand Down

0 comments on commit 341e7d4

Please sign in to comment.