From 912a98788e66b836125fe8ef37672a4de20c169c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Thu, 28 Nov 2019 23:47:53 -0800 Subject: [PATCH] Add support of media elements for Atom feeds --- reader/atom/atom.go | 69 +++++++++++---- reader/atom/parser_test.go | 171 +++++++++++++++++++++++++++++++---- reader/media/media.go | 176 +++++++++++++++++++++++++++++++++++++ reader/media/media_test.go | 110 +++++++++++++++++++++++ reader/rss/parser_test.go | 49 +++++++++++ reader/rss/rss.go | 96 +++++++------------- 6 files changed, 569 insertions(+), 102 deletions(-) create mode 100644 reader/media/media.go create mode 100644 reader/media/media_test.go diff --git a/reader/atom/atom.go b/reader/atom/atom.go index 30297e58..517b43dd 100644 --- a/reader/atom/atom.go +++ b/reader/atom/atom.go @@ -15,6 +15,7 @@ import ( "miniflux.app/logger" "miniflux.app/model" "miniflux.app/reader/date" + "miniflux.app/reader/media" "miniflux.app/reader/sanitizer" "miniflux.app/url" ) @@ -29,15 +30,15 @@ type atomFeed struct { } type atomEntry struct { - ID string `xml:"id"` - Title atomContent `xml:"title"` - Published string `xml:"published"` - Updated string `xml:"updated"` - Links []atomLink `xml:"link"` - Summary atomContent `xml:"summary"` - Content atomContent `xml:"content"` - MediaGroup atomMediaGroup `xml:"http://search.yahoo.com/mrss/ group"` - Author atomAuthor `xml:"author"` + ID string `xml:"id"` + Title atomContent `xml:"title"` + Published string `xml:"published"` + Updated string `xml:"updated"` + Links []atomLink `xml:"link"` + Summary atomContent `xml:"summary"` + Content atomContent `xml:"http://www.w3.org/2005/Atom content"` + Author atomAuthor `xml:"author"` + media.Element } type atomAuthor struct { @@ -58,10 +59,6 @@ type atomContent struct { XML string `xml:",innerxml"` } -type atomMediaGroup struct { - Description string `xml:"http://search.yahoo.com/mrss/ description"` -} - func (a *atomFeed) Transform() *model.Feed { feed := new(model.Feed) feed.FeedURL = getRelationURL(a.Links, "self") @@ -179,8 +176,9 @@ func getContent(a *atomEntry) string { return r } - if a.MediaGroup.Description != "" { - return a.MediaGroup.Description + mediaDescription := a.FirstMediaDescription() + if mediaDescription != "" { + return mediaDescription } return "" @@ -203,11 +201,48 @@ func getHash(a *atomEntry) string { func getEnclosures(a *atomEntry) model.EnclosureList { enclosures := make(model.EnclosureList, 0) + duplicates := make(map[string]bool, 0) + + for _, mediaThumbnail := range a.AllMediaThumbnails() { + if _, found := duplicates[mediaThumbnail.URL]; !found { + duplicates[mediaThumbnail.URL] = true + enclosures = append(enclosures, &model.Enclosure{ + URL: mediaThumbnail.URL, + MimeType: mediaThumbnail.MimeType(), + Size: mediaThumbnail.Size(), + }) + } + } for _, link := range a.Links { if strings.ToLower(link.Rel) == "enclosure" { - length, _ := strconv.ParseInt(link.Length, 10, 0) - enclosures = append(enclosures, &model.Enclosure{URL: link.URL, MimeType: link.Type, Size: length}) + if _, found := duplicates[link.URL]; !found { + duplicates[link.URL] = true + length, _ := strconv.ParseInt(link.Length, 10, 0) + enclosures = append(enclosures, &model.Enclosure{URL: link.URL, MimeType: link.Type, Size: length}) + } + } + } + + for _, mediaContent := range a.AllMediaContents() { + if _, found := duplicates[mediaContent.URL]; !found { + duplicates[mediaContent.URL] = true + enclosures = append(enclosures, &model.Enclosure{ + URL: mediaContent.URL, + MimeType: mediaContent.MimeType(), + Size: mediaContent.Size(), + }) + } + } + + for _, mediaPeerLink := range a.AllMediaPeerLinks() { + if _, found := duplicates[mediaPeerLink.URL]; !found { + duplicates[mediaPeerLink.URL] = true + enclosures = append(enclosures, &model.Enclosure{ + URL: mediaPeerLink.URL, + MimeType: mediaPeerLink.MimeType(), + Size: mediaPeerLink.Size(), + }) } } diff --git a/reader/atom/parser_test.go b/reader/atom/parser_test.go index 746c767a..37ab32e6 100644 --- a/reader/atom/parser_test.go +++ b/reader/atom/parser_test.go @@ -472,31 +472,30 @@ func TestParseEntryWithEnclosures(t *testing.T) { } if len(feed.Entries[0].Enclosures) != 2 { - t.Errorf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures)) + t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures)) } - if feed.Entries[0].Enclosures[0].URL != "http://www.example.org/myaudiofile.mp3" { - t.Errorf("Incorrect enclosure URL, got: %s", feed.Entries[0].Enclosures[0].URL) + expectedResults := []struct { + url string + mimeType string + size int64 + }{ + {"http://www.example.org/myaudiofile.mp3", "audio/mpeg", 1234}, + {"http://www.example.org/myaudiofile.torrent", "application/x-bittorrent", 4567}, } - if feed.Entries[0].Enclosures[0].MimeType != "audio/mpeg" { - t.Errorf("Incorrect enclosure type, got: %s", feed.Entries[0].Enclosures[0].MimeType) - } + for index, enclosure := range feed.Entries[0].Enclosures { + if expectedResults[index].url != enclosure.URL { + t.Errorf(`Unexpected enclosure URL, got %q instead of %q`, enclosure.URL, expectedResults[index].url) + } - if feed.Entries[0].Enclosures[0].Size != 1234 { - t.Errorf("Incorrect enclosure length, got: %d", feed.Entries[0].Enclosures[0].Size) - } + if expectedResults[index].mimeType != enclosure.MimeType { + t.Errorf(`Unexpected enclosure type, got %q instead of %q`, enclosure.MimeType, expectedResults[index].mimeType) + } - if feed.Entries[0].Enclosures[1].URL != "http://www.example.org/myaudiofile.torrent" { - t.Errorf("Incorrect enclosure URL, got: %s", feed.Entries[0].Enclosures[1].URL) - } - - if feed.Entries[0].Enclosures[1].MimeType != "application/x-bittorrent" { - t.Errorf("Incorrect enclosure type, got: %s", feed.Entries[0].Enclosures[1].MimeType) - } - - if feed.Entries[0].Enclosures[1].Size != 4567 { - t.Errorf("Incorrect enclosure length, got: %d", feed.Entries[0].Enclosures[1].Size) + if expectedResults[index].size != enclosure.Size { + t.Errorf(`Unexpected enclosure size, got %d instead of %d`, enclosure.Size, expectedResults[index].size) + } } } @@ -596,3 +595,137 @@ func TestParseWithInvalidCharacterEntity(t *testing.T) { t.Errorf(`Incorrect URL, got: %q`, feed.SiteURL) } } + +func TestParseMediaGroup(t *testing.T) { + data := ` + + http://www.example.org/myfeed + My Video Feed + 2005-07-15T12:00:00Z + + + + http://www.example.org/entries/1 + Some Video + 2005-07-15T12:00:00Z + + + Another title + + + Some description +A website: http://example.org/ + + + ` + + feed, err := Parse(bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if len(feed.Entries) != 1 { + t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries)) + } + + if feed.Entries[0].URL != "http://www.example.org/entries/1" { + t.Errorf("Incorrect entry URL, got: %s", feed.Entries[0].URL) + } + + if feed.Entries[0].Content != `Some description
A website: http://example.org/` { + t.Errorf("Incorrect entry content, got: %q", feed.Entries[0].Content) + } + + if len(feed.Entries[0].Enclosures) != 2 { + t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures)) + } + + expectedResults := []struct { + url string + mimeType string + size int64 + }{ + {"https://example.org/thumbnail.jpg", "image/*", 0}, + {"https://www.youtube.com/v/abcd", "application/x-shockwave-flash", 0}, + } + + for index, enclosure := range feed.Entries[0].Enclosures { + if expectedResults[index].url != enclosure.URL { + t.Errorf(`Unexpected enclosure URL, got %q instead of %q`, enclosure.URL, expectedResults[index].url) + } + + if expectedResults[index].mimeType != enclosure.MimeType { + t.Errorf(`Unexpected enclosure type, got %q instead of %q`, enclosure.MimeType, expectedResults[index].mimeType) + } + + if expectedResults[index].size != enclosure.Size { + t.Errorf(`Unexpected enclosure size, got %d instead of %d`, enclosure.Size, expectedResults[index].size) + } + } +} + +func TestParseMediaElements(t *testing.T) { + data := ` + + http://www.example.org/myfeed + My Video Feed + 2005-07-15T12:00:00Z + + + + http://www.example.org/entries/1 + Some Video + 2005-07-15T12:00:00Z + + Another title + + + Some description +A website: http://example.org/ + + ` + + feed, err := Parse(bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if len(feed.Entries) != 1 { + t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries)) + } + + if feed.Entries[0].URL != "http://www.example.org/entries/1" { + t.Errorf("Incorrect entry URL, got: %s", feed.Entries[0].URL) + } + + if feed.Entries[0].Content != `Some description
A website: http://example.org/` { + t.Errorf("Incorrect entry content, got: %q", feed.Entries[0].Content) + } + + if len(feed.Entries[0].Enclosures) != 2 { + t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures)) + } + + expectedResults := []struct { + url string + mimeType string + size int64 + }{ + {"https://example.org/thumbnail.jpg", "image/*", 0}, + {"https://www.youtube.com/v/abcd", "application/x-shockwave-flash", 0}, + } + + for index, enclosure := range feed.Entries[0].Enclosures { + if expectedResults[index].url != enclosure.URL { + t.Errorf(`Unexpected enclosure URL, got %q instead of %q`, enclosure.URL, expectedResults[index].url) + } + + if expectedResults[index].mimeType != enclosure.MimeType { + t.Errorf(`Unexpected enclosure type, got %q instead of %q`, enclosure.MimeType, expectedResults[index].mimeType) + } + + if expectedResults[index].size != enclosure.Size { + t.Errorf(`Unexpected enclosure size, got %d instead of %d`, enclosure.Size, expectedResults[index].size) + } + } +} diff --git a/reader/media/media.go b/reader/media/media.go new file mode 100644 index 00000000..d414191d --- /dev/null +++ b/reader/media/media.go @@ -0,0 +1,176 @@ +// Copyright 2019 Frédéric Guillot. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package media // import "miniflux.app/reader/media" + +import ( + "regexp" + "strconv" + "strings" +) + +var textLinkRegex = regexp.MustCompile(`(?mi)(\bhttps?:\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])`) + +// Element represents XML media elements. +type Element struct { + MediaGroups []Group `xml:"http://search.yahoo.com/mrss/ group"` + MediaContents []Content `xml:"http://search.yahoo.com/mrss/ content"` + MediaThumbnails []Thumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"` + MediaDescriptions DescriptionList `xml:"http://search.yahoo.com/mrss/ description"` + MediaPeerLinks []PeerLink `xml:"http://search.yahoo.com/mrss/ peerLink"` +} + +// AllMediaThumbnails returns all thumbnail elements merged together. +func (e *Element) AllMediaThumbnails() []Thumbnail { + var items []Thumbnail + items = append(items, e.MediaThumbnails...) + for _, mediaGroup := range e.MediaGroups { + items = append(items, mediaGroup.MediaThumbnails...) + } + return items +} + +// AllMediaContents returns all content elements merged together. +func (e *Element) AllMediaContents() []Content { + var items []Content + items = append(items, e.MediaContents...) + for _, mediaGroup := range e.MediaGroups { + items = append(items, mediaGroup.MediaContents...) + } + return items +} + +// AllMediaPeerLinks returns all peer link elements merged together. +func (e *Element) AllMediaPeerLinks() []PeerLink { + var items []PeerLink + items = append(items, e.MediaPeerLinks...) + for _, mediaGroup := range e.MediaGroups { + items = append(items, mediaGroup.MediaPeerLinks...) + } + return items +} + +// FirstMediaDescription returns the first description element. +func (e *Element) FirstMediaDescription() string { + description := e.MediaDescriptions.First() + if description != "" { + return description + } + + for _, mediaGroup := range e.MediaGroups { + description = mediaGroup.MediaDescriptions.First() + if description != "" { + return description + } + } + + return "" +} + +// Group represents a XML element "media:group". +type Group struct { + MediaContents []Content `xml:"http://search.yahoo.com/mrss/ content"` + MediaThumbnails []Thumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"` + MediaDescriptions DescriptionList `xml:"http://search.yahoo.com/mrss/ description"` + MediaPeerLinks []PeerLink `xml:"http://search.yahoo.com/mrss/ peerLink"` +} + +// Content represents a XML element "media:content". +type Content struct { + URL string `xml:"url,attr"` + Type string `xml:"type,attr"` + FileSize string `xml:"fileSize,attr"` + Medium string `xml:"medium,attr"` +} + +// MimeType returns the attachment mime type. +func (mc *Content) MimeType() string { + switch { + case mc.Type == "" && mc.Medium == "image": + return "image/*" + case mc.Type == "" && mc.Medium == "video": + return "video/*" + case mc.Type == "" && mc.Medium == "audio": + return "audio/*" + case mc.Type == "" && mc.Medium == "video": + return "video/*" + case mc.Type != "": + return mc.Type + default: + return "application/octet-stream" + } +} + +// Size returns the attachment size. +func (mc *Content) Size() int64 { + if mc.FileSize == "" { + return 0 + } + size, _ := strconv.ParseInt(mc.FileSize, 10, 0) + return size +} + +// Thumbnail represents a XML element "media:thumbnail". +type Thumbnail struct { + URL string `xml:"url,attr"` +} + +// MimeType returns the attachment mime type. +func (t *Thumbnail) MimeType() string { + return "image/*" +} + +// Size returns the attachment size. +func (t *Thumbnail) Size() int64 { + return 0 +} + +// PeerLink represents a XML element "media:peerLink". +type PeerLink struct { + URL string `xml:"href,attr"` + Type string `xml:"type,attr"` +} + +// MimeType returns the attachment mime type. +func (p *PeerLink) MimeType() string { + if p.Type != "" { + return p.Type + } + return "application/octet-stream" +} + +// Size returns the attachment size. +func (p *PeerLink) Size() int64 { + return 0 +} + +// Description represents a XML element "media:description". +type Description struct { + Type string `xml:"type,attr"` + Description string `xml:",chardata"` +} + +// HTML returns the description as HTML. +func (d *Description) HTML() string { + if d.Type == "html" { + return d.Description + } + + content := strings.Replace(d.Description, "\n", "
", -1) + return textLinkRegex.ReplaceAllString(content, `${1}`) +} + +// DescriptionList represents a list of "media:description" XML elements. +type DescriptionList []Description + +// First returns the first non-empty description. +func (dl DescriptionList) First() string { + for _, description := range dl { + contents := description.HTML() + if contents != "" { + return contents + } + } + return "" +} diff --git a/reader/media/media_test.go b/reader/media/media_test.go new file mode 100644 index 00000000..b0d2842d --- /dev/null +++ b/reader/media/media_test.go @@ -0,0 +1,110 @@ +// Copyright 2019 Frédéric Guillot. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package media // import "miniflux.app/reader/media" + +import "testing" + +func TestContentMimeType(t *testing.T) { + scenarios := []struct { + inputType, inputMedium, expectedMimeType string + }{ + {"image/png", "image", "image/png"}, + {"", "image", "image/*"}, + {"", "video", "video/*"}, + {"", "audio", "audio/*"}, + {"", "", "application/octet-stream"}, + } + + for _, scenario := range scenarios { + content := &Content{Type: scenario.inputType, Medium: scenario.inputMedium} + result := content.MimeType() + if result != scenario.expectedMimeType { + t.Errorf(`Unexpected mime type, got %q instead of %q for type=%q medium=%q`, + result, + scenario.expectedMimeType, + scenario.inputType, + scenario.inputMedium, + ) + } + } +} + +func TestContentSize(t *testing.T) { + scenarios := []struct { + inputSize string + expectedSize int64 + }{ + {"", 0}, + {"123", int64(123)}, + } + + for _, scenario := range scenarios { + content := &Content{FileSize: scenario.inputSize} + result := content.Size() + if result != scenario.expectedSize { + t.Errorf(`Unexpected size, got %d instead of %d for %q`, + result, + scenario.expectedSize, + scenario.inputSize, + ) + } + } +} + +func TestPeerLinkType(t *testing.T) { + scenarios := []struct { + inputType string + expectedMimeType string + }{ + {"", "application/octet-stream"}, + {"application/x-bittorrent", "application/x-bittorrent"}, + } + + for _, scenario := range scenarios { + peerLink := &PeerLink{Type: scenario.inputType} + result := peerLink.MimeType() + if result != scenario.expectedMimeType { + t.Errorf(`Unexpected mime type, got %q instead of %q for %q`, + result, + scenario.expectedMimeType, + scenario.inputType, + ) + } + } +} + +func TestDescription(t *testing.T) { + scenarios := []struct { + inputType string + inputContent string + expectedDescription string + }{ + {"", "", ""}, + {"html", "a c", "a c"}, + {"plain", "a\nhttp://www.example.org/", `a
http://www.example.org/`}, + } + + for _, scenario := range scenarios { + desc := &Description{Type: scenario.inputType, Description: scenario.inputContent} + result := desc.HTML() + if result != scenario.expectedDescription { + t.Errorf(`Unexpected description, got %q instead of %q for %q`, + result, + scenario.expectedDescription, + scenario.inputType, + ) + } + } +} + +func TestFirstDescription(t *testing.T) { + var descList DescriptionList + descList = append(descList, Description{}) + descList = append(descList, Description{Description: "Something"}) + + if descList.First() != "Something" { + t.Errorf(`Unexpected description`) + } +} diff --git a/reader/rss/parser_test.go b/reader/rss/parser_test.go index af98d7e7..56ae1594 100644 --- a/reader/rss/parser_test.go +++ b/reader/rss/parser_test.go @@ -771,3 +771,52 @@ func TestParseEntryWithMediaContent(t *testing.T) { } } } + +func TestParseEntryWithMediaPeerLink(t *testing.T) { + data := ` + + + My Example Feed + http://example.org + + Example Item + http://www.example.org/entries/1 + + + + ` + + feed, err := Parse(bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if len(feed.Entries) != 1 { + t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries)) + } + if len(feed.Entries[0].Enclosures) != 1 { + t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures)) + } + + expectedResults := []struct { + url string + mimeType string + size int64 + }{ + {"http://www.example.org/file.torrent", "application/x-bittorrent", 0}, + } + + for index, enclosure := range feed.Entries[0].Enclosures { + if expectedResults[index].url != enclosure.URL { + t.Errorf(`Unexpected enclosure URL, got %q instead of %q`, enclosure.URL, expectedResults[index].url) + } + + if expectedResults[index].mimeType != enclosure.MimeType { + t.Errorf(`Unexpected enclosure type, got %q instead of %q`, enclosure.MimeType, expectedResults[index].mimeType) + } + + if expectedResults[index].size != enclosure.Size { + t.Errorf(`Unexpected enclosure size, got %d instead of %d`, enclosure.Size, expectedResults[index].size) + } + } +} diff --git a/reader/rss/rss.go b/reader/rss/rss.go index 38037980..fb0c5bfa 100644 --- a/reader/rss/rss.go +++ b/reader/rss/rss.go @@ -15,6 +15,7 @@ import ( "miniflux.app/logger" "miniflux.app/model" "miniflux.app/reader/date" + "miniflux.app/reader/media" "miniflux.app/reader/sanitizer" "miniflux.app/url" ) @@ -65,62 +66,20 @@ func (enclosure *rssEnclosure) Size() int64 { } type rssItem struct { - GUID string `xml:"guid"` - Title string `xml:"title"` - Links []rssLink `xml:"link"` - OriginalLink string `xml:"http://rssnamespace.org/feedburner/ext/1.0 origLink"` - CommentLinks []rssCommentLink `xml:"comments"` - Description string `xml:"description"` - EncodedContent string `xml:"http://purl.org/rss/1.0/modules/content/ encoded"` - PubDate string `xml:"pubDate"` - Date string `xml:"http://purl.org/dc/elements/1.1/ date"` - Authors []rssAuthor `xml:"author"` - Creator string `xml:"http://purl.org/dc/elements/1.1/ creator"` - EnclosureLinks []rssEnclosure `xml:"enclosure"` - OrigEnclosureLink string `xml:"http://rssnamespace.org/feedburner/ext/1.0 origEnclosureLink"` - MediaGroup []rssMediaGroup `xml:"http://search.yahoo.com/mrss/ group"` - MediaContents []rssMediaContent `xml:"http://search.yahoo.com/mrss/ content"` - MediaThumbnails []rssMediaThumbnails `xml:"http://search.yahoo.com/mrss/ thumbnail"` -} - -type rssMediaGroup struct { - MediaList []rssMediaContent `xml:"content"` -} - -type rssMediaContent struct { - URL string `xml:"url,attr"` - Type string `xml:"type,attr"` - FileSize string `xml:"fileSize,attr"` - Medium string `xml:"medium,attr"` -} - -func (mediaContent *rssMediaContent) MimeType() string { - switch { - case mediaContent.Type == "" && mediaContent.Medium == "image": - return "image/*" - case mediaContent.Type == "" && mediaContent.Medium == "video": - return "video/*" - case mediaContent.Type == "" && mediaContent.Medium == "audio": - return "audio/*" - case mediaContent.Type == "" && mediaContent.Medium == "video": - return "video/*" - case mediaContent.Type != "": - return mediaContent.Type - default: - return "application/octet-stream" - } -} - -func (mediaContent *rssMediaContent) Size() int64 { - if mediaContent.FileSize == "" { - return 0 - } - size, _ := strconv.ParseInt(mediaContent.FileSize, 10, 0) - return size -} - -type rssMediaThumbnails struct { - URL string `xml:"url,attr"` + GUID string `xml:"guid"` + Title string `xml:"title"` + Links []rssLink `xml:"link"` + OriginalLink string `xml:"http://rssnamespace.org/feedburner/ext/1.0 origLink"` + CommentLinks []rssCommentLink `xml:"comments"` + Description string `xml:"description"` + EncodedContent string `xml:"http://purl.org/rss/1.0/modules/content/ encoded"` + PubDate string `xml:"pubDate"` + Date string `xml:"http://purl.org/dc/elements/1.1/ date"` + Authors []rssAuthor `xml:"author"` + Creator string `xml:"http://purl.org/dc/elements/1.1/ creator"` + EnclosureLinks []rssEnclosure `xml:"enclosure"` + OrigEnclosureLink string `xml:"http://rssnamespace.org/feedburner/ext/1.0 origEnclosureLink"` + media.Element } func (r *rssFeed) SiteURL() string { @@ -253,13 +212,13 @@ func (r *rssItem) Enclosures() model.EnclosureList { enclosures := make(model.EnclosureList, 0) duplicates := make(map[string]bool, 0) - for _, mediaThumbnail := range r.MediaThumbnails { + for _, mediaThumbnail := range r.AllMediaThumbnails() { if _, found := duplicates[mediaThumbnail.URL]; !found { duplicates[mediaThumbnail.URL] = true enclosures = append(enclosures, &model.Enclosure{ URL: mediaThumbnail.URL, - MimeType: "image/*", - Size: 0, + MimeType: mediaThumbnail.MimeType(), + Size: mediaThumbnail.Size(), }) } } @@ -285,13 +244,7 @@ func (r *rssItem) Enclosures() model.EnclosureList { } } - for _, mediaContentItem := range r.MediaGroup { - for _, mediaContent := range mediaContentItem.MediaList { - r.MediaContents = append(r.MediaContents, mediaContent) - } - } - - for _, mediaContent := range r.MediaContents { + for _, mediaContent := range r.AllMediaContents() { if _, found := duplicates[mediaContent.URL]; !found { duplicates[mediaContent.URL] = true enclosures = append(enclosures, &model.Enclosure{ @@ -302,6 +255,17 @@ func (r *rssItem) Enclosures() model.EnclosureList { } } + for _, mediaPeerLink := range r.AllMediaPeerLinks() { + if _, found := duplicates[mediaPeerLink.URL]; !found { + duplicates[mediaPeerLink.URL] = true + enclosures = append(enclosures, &model.Enclosure{ + URL: mediaPeerLink.URL, + MimeType: mediaPeerLink.MimeType(), + Size: mediaPeerLink.Size(), + }) + } + } + return enclosures }