diff --git a/internal/reader/rewrite/content_rewrite_functions.go b/internal/reader/rewrite/content_rewrite_functions.go
index 3290d60b..35db76c3 100644
--- a/internal/reader/rewrite/content_rewrite_functions.go
+++ b/internal/reader/rewrite/content_rewrite_functions.go
@@ -273,9 +273,12 @@ func getYoutubVideoIDFromURL(entryURL string) string {
return ""
}
+func buildVideoPlayerIframe(absoluteVideoURL string) string {
+ return ``
+}
+
func addVideoPlayerIframe(absoluteVideoURL, entryContent string) string {
- video := ``
- return video + `
` + entryContent
+ return buildVideoPlayerIframe(absoluteVideoURL) + `
` + entryContent
}
func addYoutubeVideoRewriteRule(entryURL, entryContent string) string {
@@ -292,29 +295,25 @@ func addYoutubeVideoUsingInvidiousPlayer(entryURL, entryContent string) string {
return entryContent
}
+// For reference: https://github.com/miniflux/v2/pull/1314
func addYoutubeVideoFromId(entryContent string) string {
matches := youtubeIdRegex.FindAllStringSubmatch(entryContent, -1)
if matches == nil {
return entryContent
}
- sb := strings.Builder{}
+ videoPlayerHTML := ""
for _, match := range matches {
if len(match) == 2 {
- sb.WriteString(`
`)
+ videoPlayerHTML += buildVideoPlayerIframe(config.Opts.YouTubeEmbedUrlOverride()+match[1]) + "
"
}
}
- sb.WriteString(entryContent)
- return sb.String()
+ return videoPlayerHTML + entryContent
}
func addInvidiousVideo(entryURL, entryContent string) string {
matches := invidioRegex.FindStringSubmatch(entryURL)
if len(matches) == 3 {
- video := ``
- return video + `
` + entryContent
+ return addVideoPlayerIframe(`https://`+matches[1]+`/embed/`+matches[2], entryContent)
}
return entryContent
}
diff --git a/internal/reader/rewrite/content_rewrite_test.go b/internal/reader/rewrite/content_rewrite_test.go
index b0b7564d..4ee0817d 100644
--- a/internal/reader/rewrite/content_rewrite_test.go
+++ b/internal/reader/rewrite/content_rewrite_test.go
@@ -195,6 +195,138 @@ func TestRewriteYoutubeShortLinkUsingInvidious(t *testing.T) {
}
}
+func TestAddYoutubeVideoFromId(t *testing.T) {
+ config.Opts = config.NewOptions()
+
+ scenarios := map[string]string{
+ // Test with single YouTube ID
+ `Some content with youtube ID `: `
Some content with youtube ID `,
+
+ // Test with multiple YouTube IDs
+ `Content with youtube_id: "dQw4w9WgXcQ" and youtube_id: "jNQXAC9IVRw"`: `
Content with youtube_id: "dQw4w9WgXcQ" and youtube_id: "jNQXAC9IVRw"`,
+
+ // Test with YouTube ID using equals sign
+ `Some content with youtube_id = "dQw4w9WgXcQ"`: `
Some content with youtube_id = "dQw4w9WgXcQ"`,
+
+ // Test with spaces around delimiters
+ `Some content with youtube_id : "dQw4w9WgXcQ"`: `
Some content with youtube_id : "dQw4w9WgXcQ"`,
+
+ // Test with YouTube ID without quotes (regex requires quotes)
+ `Some content with youtube_id: dQw4w9WgXcQ and more`: `Some content with youtube_id: dQw4w9WgXcQ and more`,
+
+ // Test with no YouTube ID
+ `Some regular content without any video ID`: `Some regular content without any video ID`,
+
+ // Test with invalid YouTube ID (wrong length)
+ `Some content with youtube_id: "invalid"`: `Some content with youtube_id: "invalid"`,
+
+ // Test with empty content
+ ``: ``,
+ }
+
+ for input, expected := range scenarios {
+ actual := addYoutubeVideoFromId(input)
+ if actual != expected {
+ t.Errorf(`addYoutubeVideoFromId test failed for input "%s"`, input)
+ t.Errorf(`Expected: "%s"`, expected)
+ t.Errorf(`Actual: "%s"`, actual)
+ }
+ }
+}
+
+func TestAddYoutubeVideoFromIdWithCustomEmbedURL(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("YOUTUBE_EMBED_URL_OVERRIDE", "https://invidious.custom/embed/")
+
+ var err error
+ parser := config.NewParser()
+ config.Opts, err = parser.ParseEnvironmentVariables()
+
+ if err != nil {
+ t.Fatalf(`Parsing failure: %v`, err)
+ }
+
+ input := `Some content with youtube_id: "dQw4w9WgXcQ"`
+ expected := `
Some content with youtube_id: "dQw4w9WgXcQ"`
+
+ actual := addYoutubeVideoFromId(input)
+ if actual != expected {
+ t.Errorf(`addYoutubeVideoFromId with custom embed URL failed`)
+ t.Errorf(`Expected: "%s"`, expected)
+ t.Errorf(`Actual: "%s"`, actual)
+ }
+}
+
+func TestAddInvidiousVideo(t *testing.T) {
+ scenarios := map[string][]string{
+ // Test with various Invidious instances
+ "https://invidious.io/watch?v=dQw4w9WgXcQ": {
+ "Some video content",
+ `
Some video content`,
+ },
+ "https://yewtu.be/watch?v=jNQXAC9IVRw": {
+ "Another video description",
+ `
Another video description`,
+ },
+ "http://invidious.snopyta.org/watch?v=dQw4w9WgXcQ": {
+ "HTTP instance test",
+ `
HTTP instance test`,
+ },
+ "https://youtube.com/watch?v=dQw4w9WgXcQ": {
+ "YouTube URL (also matches regex)",
+ `
YouTube URL (also matches regex)`,
+ },
+ "https://example.org/watch?v=dQw4w9WgXcQ": {
+ "Any domain with watch pattern",
+ `
Any domain with watch pattern`,
+ },
+
+ // Test with query parameters
+ "https://invidious.io/watch?v=dQw4w9WgXcQ&t=30s": {
+ "Video with timestamp",
+ `
Video with timestamp`,
+ },
+
+ // Test with more complex query parameters
+ "https://invidious.io/watch?v=dQw4w9WgXcQ&t=30s&autoplay=1": {
+ "Video with multiple parameters",
+ `
Video with multiple parameters`,
+ },
+
+ // Test with non-matching URLs (should return content unchanged)
+ "https://invidious.io/": {
+ "Invidious homepage",
+ "Invidious homepage",
+ },
+ "https://invidious.io/some-other-page": {
+ "Other page",
+ "Other page",
+ },
+ "https://invidious.io/search?q=test": {
+ "Search page",
+ "Search page",
+ },
+
+ // Test with empty content
+ "https://empty.invidious.io/watch?v=dQw4w9WgXcQ": {
+ "",
+ `
`,
+ },
+ }
+
+ for entryURL, testData := range scenarios {
+ entryContent := testData[0]
+ expected := testData[1]
+
+ actual := addInvidiousVideo(entryURL, entryContent)
+ if actual != expected {
+ t.Errorf(`addInvidiousVideo test failed for URL "%s" and content "%s"`, entryURL, entryContent)
+ t.Errorf(`Expected: "%s"`, expected)
+ t.Errorf(`Actual: "%s"`, actual)
+ }
+ }
+}
+
func TestRewriteWithInexistingCustomRule(t *testing.T) {
controlEntry := &model.Entry{
URL: "https://www.youtube.com/watch?v=1234",