Browse Source

Allow using alternative links for YT, HN and reddit

Svilen Markov 1 year ago
parent
commit
44a153d30a

+ 45 - 0
docs/configuration.md

@@ -434,6 +434,7 @@ Preview:
 | ---- | ---- | -------- | ------- |
 | channels | array | yes | |
 | limit | integer | no | 25 |
+| video-url-template | string | no | https://www.youtube.com/watch?v={VIDEO-ID} |
 
 ##### `channels`
 A list of channel IDs. One way of getting the ID of a channel is going to the channel's page and clicking on its description:
@@ -447,6 +448,17 @@ Then scroll down and click on "Share channel", then "Copy channel ID":
 ##### `limit`
 The maximum number of videos to show.
 
+##### `video-url-template`
+Used to replace the default link for videos. Useful when you're running your own YouTube front-end. Example:
+
+```yaml
+video-url-template: https://invidious.your-domain.com/watch?v={VIDEO-ID}
+```
+
+Placeholders:
+
+`{VIDEO-ID}` - the ID of the video
+
 ### Hacker News
 Display a list of posts from [Hacker News](https://news.ycombinator.com/).
 
@@ -466,6 +478,19 @@ Preview:
 | ---- | ---- | -------- | ------- |
 | limit | integer | no | 15 |
 | collapse-after | integer | no | 5 |
+| comments-url-template | string | no | https://news.ycombinator.com/item?id={POST-ID} |
+
+##### `comments-url-template`
+Used to replace the default link for post comments. Useful if you want to use an alternative front-end. Example:
+
+```yaml
+comments-url-template: https://www.hckrnws.com/stories/{POST-PATH}
+```
+
+Placeholders:
+
+`{POST-ID}` - the ID of the post
+
 
 ### Reddit
 Display a list of posts from a specific subreddit.
@@ -488,6 +513,7 @@ Example:
 | style | string | no | vertical-list |
 | limit | integer | no | 15 |
 | collapse-after | integer | no | 5 |
+| comments-url-template | string | no | https://www.reddit.com/{POST-PATH} |
 
 ##### `subreddit`
 The subreddit for which to fetch the posts from.
@@ -513,6 +539,25 @@ The maximum number of posts to show.
 ##### `collapse-after`
 How many posts are visible before the "SHOW MORE" button appears. Set to `-1` to never collapse. Not available when using the `vertical-cards` and `horizontal-cards` styles.
 
+##### `comments-url-template`
+Used to replace the default link for post comments. Useful if you want to use the old Reddit design or any other 3rd party front-end. Example:
+
+```yaml
+comments-url-template: https://old.reddit.com/{POST-PATH}
+```
+
+Placeholders:
+
+`{POST-PATH}` - the full path to the post, such as:
+
+```
+r/selfhosted/comments/bsp01i/welcome_to_rselfhosted_please_read_this_first/
+```
+
+`{POST-ID}` - the ID that comes after `/comments/`
+
+`{SUBREDDIT}` - the subreddit name
+
 ### Weather
 Display weather information for a specific location. The data is provided by https://open-meteo.com/.
 

+ 13 - 4
internal/feed/hacker-news.go

@@ -5,6 +5,7 @@ import (
 	"log/slog"
 	"net/http"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -28,7 +29,7 @@ func getHackerNewsTopPostIds() ([]int, error) {
 	return response, nil
 }
 
-func getHackerNewsPostsFromIds(postIds []int) (ForumPosts, error) {
+func getHackerNewsPostsFromIds(postIds []int, commentsUrlTemplate string) (ForumPosts, error) {
 	requests := make([]*http.Request, len(postIds))
 
 	for i, id := range postIds {
@@ -52,9 +53,17 @@ func getHackerNewsPostsFromIds(postIds []int) (ForumPosts, error) {
 			continue
 		}
 
+		var commentsUrl string
+
+		if commentsUrlTemplate == "" {
+			commentsUrl = "https://news.ycombinator.com/item?id=" + strconv.Itoa(results[i].Id)
+		} else {
+			commentsUrl = strings.ReplaceAll(commentsUrlTemplate, "{POST-ID}", strconv.Itoa(results[i].Id))
+		}
+
 		posts = append(posts, ForumPost{
 			Title:           results[i].Title,
-			DiscussionUrl:   "https://news.ycombinator.com/item?id=" + strconv.Itoa(results[i].Id),
+			DiscussionUrl:   commentsUrl,
 			TargetUrl:       results[i].TargetUrl,
 			TargetUrlDomain: extractDomainFromUrl(results[i].TargetUrl),
 			CommentCount:    results[i].CommentCount,
@@ -74,7 +83,7 @@ func getHackerNewsPostsFromIds(postIds []int) (ForumPosts, error) {
 	return posts, nil
 }
 
-func FetchHackerNewsTopPosts(limit int) (ForumPosts, error) {
+func FetchHackerNewsTopPosts(limit int, commentsUrlTemplate string) (ForumPosts, error) {
 	postIds, err := getHackerNewsTopPostIds()
 
 	if err != nil {
@@ -85,5 +94,5 @@ func FetchHackerNewsTopPosts(limit int) (ForumPosts, error) {
 		postIds = postIds[:limit]
 	}
 
-	return getHackerNewsPostsFromIds(postIds)
+	return getHackerNewsPostsFromIds(postIds, commentsUrlTemplate)
 }

+ 14 - 2
internal/feed/reddit.go

@@ -5,6 +5,7 @@ import (
 	"html"
 	"net/http"
 	"net/url"
+	"strings"
 	"time"
 )
 
@@ -12,6 +13,7 @@ type subredditResponseJson struct {
 	Data struct {
 		Children []struct {
 			Data struct {
+				Id            string  `json:"id"`
 				Title         string  `json:"title"`
 				Upvotes       int     `json:"ups"`
 				Url           string  `json:"url"`
@@ -28,7 +30,7 @@ type subredditResponseJson struct {
 	} `json:"data"`
 }
 
-func FetchSubredditPosts(subreddit string) (ForumPosts, error) {
+func FetchSubredditPosts(subreddit string, commentsUrlTemplate string) (ForumPosts, error) {
 	requestUrl := fmt.Sprintf("https://www.reddit.com/r/%s/hot.json", url.QueryEscape(subreddit))
 	request, err := http.NewRequest("GET", requestUrl, nil)
 
@@ -57,9 +59,19 @@ func FetchSubredditPosts(subreddit string) (ForumPosts, error) {
 			continue
 		}
 
+		var commentsUrl string
+
+		if commentsUrlTemplate == "" {
+			commentsUrl = "https://www.reddit.com" + post.Permalink
+		} else {
+			commentsUrl = strings.ReplaceAll(commentsUrlTemplate, "{SUBREDDIT}", subreddit)
+			commentsUrl = strings.ReplaceAll(commentsUrl, "{POST-ID}", post.Id)
+			commentsUrl = strings.ReplaceAll(commentsUrl, "{POST-PATH}", strings.TrimLeft(post.Permalink, "/"))
+		}
+
 		forumPost := ForumPost{
 			Title:           html.UnescapeString(post.Title),
-			DiscussionUrl:   "https://www.reddit.com" + post.Permalink,
+			DiscussionUrl:   commentsUrl,
 			TargetUrlDomain: post.Domain,
 			CommentCount:    post.CommentsCount,
 			Score:           post.Upvotes,

+ 17 - 2
internal/feed/youtube.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"log/slog"
 	"net/http"
+	"net/url"
 	"strings"
 	"time"
 )
@@ -38,7 +39,7 @@ func parseYoutubeFeedTime(t string) time.Time {
 	return parsedTime
 }
 
-func FetchYoutubeChannelUploads(channelIds []string) (Videos, error) {
+func FetchYoutubeChannelUploads(channelIds []string, videoUrlTemplate string) (Videos, error) {
 	requests := make([]*http.Request, 0, len(channelIds))
 
 	for i := range channelIds {
@@ -75,10 +76,24 @@ func FetchYoutubeChannelUploads(channelIds []string) (Videos, error) {
 				continue
 			}
 
+			var videoUrl string
+
+			if videoUrlTemplate == "" {
+				videoUrl = video.Link.Href
+			} else {
+				parsedUrl, err := url.Parse(video.Link.Href)
+
+				if err == nil {
+					videoUrl = strings.ReplaceAll(videoUrlTemplate, "{VIDEO-ID}", parsedUrl.Query().Get("v"))
+				} else {
+					videoUrl = "#"
+				}
+			}
+
 			videos = append(videos, Video{
 				ThumbnailUrl: video.Group.Thumbnail.Url,
 				Title:        video.Title,
-				Url:          video.Link.Href,
+				Url:          videoUrl,
 				Author:       response.Channel,
 				AuthorUrl:    response.ChannelLink.Href + "/videos",
 				TimePosted:   parseYoutubeFeedTime(video.Published),

+ 6 - 5
internal/widget/hacker-news.go

@@ -10,10 +10,11 @@ import (
 )
 
 type HackerNews struct {
-	widgetBase    `yaml:",inline"`
-	Posts         feed.ForumPosts `yaml:"-"`
-	Limit         int             `yaml:"limit"`
-	CollapseAfter int             `yaml:"collapse-after"`
+	widgetBase          `yaml:",inline"`
+	Posts               feed.ForumPosts `yaml:"-"`
+	Limit               int             `yaml:"limit"`
+	CollapseAfter       int             `yaml:"collapse-after"`
+	CommentsUrlTemplate string          `yaml:"comments-url-template"`
 }
 
 func (widget *HackerNews) Initialize() error {
@@ -31,7 +32,7 @@ func (widget *HackerNews) Initialize() error {
 }
 
 func (widget *HackerNews) Update(ctx context.Context) {
-	posts, err := feed.FetchHackerNewsTopPosts(40)
+	posts, err := feed.FetchHackerNewsTopPosts(40, widget.CommentsUrlTemplate)
 
 	if !widget.canContinueUpdateAfterHandlingErr(err) {
 		return

+ 8 - 7
internal/widget/reddit.go

@@ -11,12 +11,13 @@ import (
 )
 
 type Reddit struct {
-	widgetBase    `yaml:",inline"`
-	Posts         feed.ForumPosts `yaml:"-"`
-	Subreddit     string          `yaml:"subreddit"`
-	Style         string          `yaml:"style"`
-	Limit         int             `yaml:"limit"`
-	CollapseAfter int             `yaml:"collapse-after"`
+	widgetBase          `yaml:",inline"`
+	Posts               feed.ForumPosts `yaml:"-"`
+	Subreddit           string          `yaml:"subreddit"`
+	Style               string          `yaml:"style"`
+	CommentsUrlTemplate string          `yaml:"comments-url-template"`
+	Limit               int             `yaml:"limit"`
+	CollapseAfter       int             `yaml:"collapse-after"`
 }
 
 func (widget *Reddit) Initialize() error {
@@ -38,7 +39,7 @@ func (widget *Reddit) Initialize() error {
 }
 
 func (widget *Reddit) Update(ctx context.Context) {
-	posts, err := feed.FetchSubredditPosts(widget.Subreddit)
+	posts, err := feed.FetchSubredditPosts(widget.Subreddit, widget.CommentsUrlTemplate)
 
 	if !widget.canContinueUpdateAfterHandlingErr(err) {
 		return

+ 6 - 5
internal/widget/videos.go

@@ -10,10 +10,11 @@ import (
 )
 
 type Videos struct {
-	widgetBase `yaml:",inline"`
-	Videos     feed.Videos `yaml:"-"`
-	Channels   []string    `yaml:"channels"`
-	Limit      int         `yaml:"limit"`
+	widgetBase       `yaml:",inline"`
+	Videos           feed.Videos `yaml:"-"`
+	VideoUrlTemplate string      `yaml:"video-url-template"`
+	Channels         []string    `yaml:"channels"`
+	Limit            int         `yaml:"limit"`
 }
 
 func (widget *Videos) Initialize() error {
@@ -27,7 +28,7 @@ func (widget *Videos) Initialize() error {
 }
 
 func (widget *Videos) Update(ctx context.Context) {
-	videos, err := feed.FetchYoutubeChannelUploads(widget.Channels)
+	videos, err := feed.FetchYoutubeChannelUploads(widget.Channels, widget.VideoUrlTemplate)
 
 	if !widget.canContinueUpdateAfterHandlingErr(err) {
 		return