diff --git a/docs/configuration.md b/docs/configuration.md index 0c14525..a46569a 100644 --- a/docs/configuration.md +++ b/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/. diff --git a/internal/feed/hacker-news.go b/internal/feed/hacker-news.go index 8e7ad73..baacdf2 100644 --- a/internal/feed/hacker-news.go +++ b/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) } diff --git a/internal/feed/reddit.go b/internal/feed/reddit.go index b8449f9..c8ff4c6 100644 --- a/internal/feed/reddit.go +++ b/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, diff --git a/internal/feed/youtube.go b/internal/feed/youtube.go index e22b8bb..e478259 100644 --- a/internal/feed/youtube.go +++ b/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), diff --git a/internal/widget/hacker-news.go b/internal/widget/hacker-news.go index 9870e74..1025d06 100644 --- a/internal/widget/hacker-news.go +++ b/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 diff --git a/internal/widget/reddit.go b/internal/widget/reddit.go index 3287fcf..b884ac6 100644 --- a/internal/widget/reddit.go +++ b/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 diff --git a/internal/widget/videos.go b/internal/widget/videos.go index 2652768..a472f0e 100644 --- a/internal/widget/videos.go +++ b/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