Просмотр исходного кода

feat(rewrite): add enclosure links

When subscribing to podcasts or videocasts (or any feeds with enclosures), Miniflux presents audio/video controls on top, and links on bottom. But it's is not a part of the article, it is Miniflux-only UI modification. A new rewrite rule is added to expose enclosure links to the content, so that it can be accessed outsite of Miniflux, specifically on any native mobile RSS apps. That way, it is now possible to access media files to open them eg. in native media player apps.
Mateusz Jabłoński 1 неделя назад
Родитель
Сommit
cf7474a2d3

+ 2 - 0
internal/reader/rewrite/content_rewrite.go

@@ -76,6 +76,8 @@ func (rule rule) applyRule(entryURL string, entry *model.Entry) {
 				slog.String("entry_url", entryURL),
 			)
 		}
+	case "add_enclosure_links":
+		entry.Content = addEnclosureLinks(entry)
 	case "add_castopod_episode":
 		entry.Content = addCastopodEpisode(entryURL, entry.Content)
 	case "base64_decode":

+ 19 - 0
internal/reader/rewrite/content_rewrite_functions.go

@@ -15,6 +15,7 @@ import (
 	"unicode"
 
 	"miniflux.app/v2/internal/config"
+	"miniflux.app/v2/internal/model"
 
 	nethtml "golang.org/x/net/html"
 
@@ -355,6 +356,24 @@ func addPDFLink(entryURL, entryContent string) string {
 	return entryContent
 }
 
+func addEnclosureLinks(entry *model.Entry) string {
+	var links strings.Builder
+
+	for _, enclosure := range entry.Enclosures {
+		if enclosure.URL == "" {
+			continue
+		}
+
+		enclosureURL := html.EscapeString(enclosure.URL)
+		links.WriteString(`<li><a href="` + enclosureURL + `">` + enclosureURL + `</a></li>`)
+	}
+
+	if links.Len() > 0 {
+		return entry.Content + "<hr/><ul>" + strings.TrimSpace(links.String()) + "</ul>"
+	}
+	return entry.Content
+}
+
 func replaceTextLinks(input string) string {
 	return textLinkRegex.ReplaceAllString(input, `<a href="${1}">${1}</a>`)
 }

+ 54 - 0
internal/reader/rewrite/content_rewrite_test.go

@@ -832,6 +832,60 @@ func TestRewriteAddCastopodEpisode(t *testing.T) {
 	}
 }
 
+func TestRewriteAddEnclosureLinks(t *testing.T) {
+	controlEntry := &model.Entry{
+		URL:   "https://example.org/article",
+		Title: `A title`,
+		Content: `Article Content<hr/><ul>` +
+			`<li><a href="https://example.org/episode.mp3?token=a&amp;b=c">https://example.org/episode.mp3?token=a&amp;b=c</a></li>` +
+			`<li><a href="https://example.org/video.mp4">https://example.org/video.mp4</a></li></ul>`,
+		Enclosures: model.EnclosureList{
+			{URL: "https://example.org/episode.mp3?token=a&b=c", MimeType: "audio/mpeg"},
+			{URL: "https://example.org/video.mp4", MimeType: "video/mp4"},
+			{URL: "", MimeType: "application/pdf"},
+		},
+	}
+	testEntry := &model.Entry{
+		URL:     "https://example.org/article",
+		Title:   `A title`,
+		Content: `Article Content`,
+		Enclosures: model.EnclosureList{
+			{URL: "https://example.org/episode.mp3?token=a&b=c", MimeType: "audio/mpeg"},
+			{URL: "https://example.org/video.mp4", MimeType: "video/mp4"},
+			{URL: "", MimeType: "application/pdf"},
+		},
+	}
+	ApplyContentRewriteRules(testEntry, `add_enclosure_links`)
+
+	if !reflect.DeepEqual(testEntry, controlEntry) {
+		t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry)
+	}
+}
+
+func TestRewriteAddEnclosureLinksWithoutEnclosureURL(t *testing.T) {
+	controlEntry := &model.Entry{
+		URL:     "https://example.org/article",
+		Title:   `A title`,
+		Content: `Article Content`,
+		Enclosures: model.EnclosureList{
+			{URL: "", MimeType: "audio/mpeg"},
+		},
+	}
+	testEntry := &model.Entry{
+		URL:     "https://example.org/article",
+		Title:   `A title`,
+		Content: `Article Content`,
+		Enclosures: model.EnclosureList{
+			{URL: "", MimeType: "audio/mpeg"},
+		},
+	}
+	ApplyContentRewriteRules(testEntry, `add_enclosure_links`)
+
+	if !reflect.DeepEqual(testEntry, controlEntry) {
+		t.Errorf(`Not expected output: got "%+v" instead of "%+v"`, testEntry, controlEntry)
+	}
+}
+
 func TestRewriteBase64Decode(t *testing.T) {
 	controlEntry := &model.Entry{
 		URL:     "https://example.org/article",