diff --git a/internal/reader/rss/podcast.go b/internal/reader/rss/podcast.go index ae6780f1..f64dd4cd 100644 --- a/internal/reader/rss/podcast.go +++ b/internal/reader/rss/podcast.go @@ -3,7 +3,12 @@ package rss // import "miniflux.app/v2/internal/reader/rss" -import "strings" +import ( + "fmt" + "math" + "strconv" + "strings" +) // PodcastFeedElement represents iTunes and GooglePlay feed XML elements. // Specs: @@ -21,6 +26,7 @@ type PodcastFeedElement struct { type PodcastEntryElement struct { Subtitle string `xml:"http://www.itunes.com/dtds/podcast-1.0.dtd subtitle"` Summary string `xml:"http://www.itunes.com/dtds/podcast-1.0.dtd summary"` + Duration string `xml:"http://www.itunes.com/dtds/podcast-1.0.dtd duration"` GooglePlayDescription string `xml:"http://www.google.com/schemas/play-podcasts/1.0 description"` } @@ -67,3 +73,26 @@ 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 + } + + for i, durationPart := range durationParts { + durationPartValue, err := strconv.Atoi(durationPart) + if err != nil { + return 0, invalidDurationFormatErr + } + + sumSeconds += int(math.Pow(60, float64(len(durationParts)-i-1))) * durationPartValue + } + + return sumSeconds / 60, nil +} diff --git a/internal/reader/rss/rss.go b/internal/reader/rss/rss.go index add51a6c..7edf91db 100644 --- a/internal/reader/rss/rss.go +++ b/internal/reader/rss/rss.go @@ -199,6 +199,9 @@ func (r *rssItem) Transform() *model.Entry { entry.Title = r.entryTitle() entry.Enclosures = r.entryEnclosures() entry.Tags = r.entryCategories() + if duration, err := normalizeDuration(r.Duration); err == nil { + entry.ReadingTime = duration + } return entry }