Allow specifying playlist in videos widget

Co-authored-by: vishalkadam47 <vishal@jeevops.com>
This commit is contained in:
Svilen Markov 2024-12-06 10:53:08 +00:00
parent 1922e1e895
commit e3bdc73013
2 changed files with 23 additions and 12 deletions

View File

@ -587,7 +587,15 @@ Preview:
| video-url-template | string | no | https://www.youtube.com/watch?v={VIDEO-ID} | | video-url-template | string | no | https://www.youtube.com/watch?v={VIDEO-ID} |
##### `channels` ##### `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: A list of channel or playlist IDs. To specify a playlist, use the `playlist:` prefix like such:
```yaml
channels:
- playlist:PL8mG-RkN2uTyZZ00ObwZxxoG_nJbs3qec
- playlist:PL8mG-RkN2uTxTK4m_Vl2dYR9yE41kRdBg
```
One way of getting the ID of a channel is going to the channel's page and clicking on its description:
![](images/videos-channel-description-example.png) ![](images/videos-channel-description-example.png)

View File

@ -12,6 +12,8 @@ import (
"time" "time"
) )
const videosWidgetPlaylistPrefix = "playlist:"
var ( var (
videosWidgetTemplate = mustParseTemplate("videos.html", "widget-base.html", "video-card-contents.html") videosWidgetTemplate = mustParseTemplate("videos.html", "widget-base.html", "video-card-contents.html")
videosWidgetGridTemplate = mustParseTemplate("videos-grid.html", "widget-base.html", "video-card-contents.html") videosWidgetGridTemplate = mustParseTemplate("videos-grid.html", "widget-base.html", "video-card-contents.html")
@ -43,7 +45,7 @@ func (widget *videosWidget) initialize() error {
} }
func (widget *videosWidget) update(ctx context.Context) { func (widget *videosWidget) update(ctx context.Context) {
videos, err := FetchYoutubeChannelUploads(widget.Channels, widget.VideoUrlTemplate, widget.IncludeShorts) videos, err := fetchYoutubeChannelUploads(widget.Channels, widget.VideoUrlTemplate, widget.IncludeShorts)
if !widget.canContinueUpdateAfterHandlingErr(err) { if !widget.canContinueUpdateAfterHandlingErr(err) {
return return
@ -110,16 +112,19 @@ func (v videoList) sortByNewest() videoList {
return v return v
} }
func FetchYoutubeChannelUploads(channelIds []string, videoUrlTemplate string, includeShorts bool) (videoList, error) { func fetchYoutubeChannelUploads(channelOrPlaylistIDs []string, videoUrlTemplate string, includeShorts bool) (videoList, error) {
requests := make([]*http.Request, 0, len(channelIds)) requests := make([]*http.Request, 0, len(channelOrPlaylistIDs))
for i := range channelIds { for i := range channelOrPlaylistIDs {
var feedUrl string var feedUrl string
if !includeShorts && strings.HasPrefix(channelIds[i], "UC") { if strings.HasPrefix(channelOrPlaylistIDs[i], videosWidgetPlaylistPrefix) {
playlistId := strings.Replace(channelIds[i], "UC", "UULF", 1) feedUrl = "https://www.youtube.com/feeds/videos.xml?playlist_id=" +
strings.TrimPrefix(channelOrPlaylistIDs[i], videosWidgetPlaylistPrefix)
} else if !includeShorts && strings.HasPrefix(channelOrPlaylistIDs[i], "UC") {
playlistId := strings.Replace(channelOrPlaylistIDs[i], "UC", "UULF", 1)
feedUrl = "https://www.youtube.com/feeds/videos.xml?playlist_id=" + playlistId feedUrl = "https://www.youtube.com/feeds/videos.xml?playlist_id=" + playlistId
} else { } else {
feedUrl = "https://www.youtube.com/feeds/videos.xml?channel_id=" + channelIds[i] feedUrl = "https://www.youtube.com/feeds/videos.xml?channel_id=" + channelOrPlaylistIDs[i]
} }
request, _ := http.NewRequest("GET", feedUrl, nil) request, _ := http.NewRequest("GET", feedUrl, nil)
@ -127,20 +132,18 @@ func FetchYoutubeChannelUploads(channelIds []string, videoUrlTemplate string, in
} }
job := newJob(decodeXmlFromRequestTask[youtubeFeedResponseXml](defaultHTTPClient), requests).withWorkers(30) job := newJob(decodeXmlFromRequestTask[youtubeFeedResponseXml](defaultHTTPClient), requests).withWorkers(30)
responses, errs, err := workerPoolDo(job) responses, errs, err := workerPoolDo(job)
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %v", errNoContent, err) return nil, fmt.Errorf("%w: %v", errNoContent, err)
} }
videos := make(videoList, 0, len(channelIds)*15) videos := make(videoList, 0, len(channelOrPlaylistIDs)*15)
var failed int var failed int
for i := range responses { for i := range responses {
if errs[i] != nil { if errs[i] != nil {
failed++ failed++
slog.Error("Failed to fetch youtube feed", "channel", channelIds[i], "error", errs[i]) slog.Error("Failed to fetch youtube feed", "channel", channelOrPlaylistIDs[i], "error", errs[i])
continue continue
} }