Add filters for rss and videos widgets

pull/842/head
Svilen Markov 2025-09-06 13:42:27 +07:00
parent ad60d52264
commit a7132946a3
5 changed files with 59 additions and 6 deletions

@ -1,8 +1,11 @@
{{ template "widget-base.html" . }}
{{ define "widget-content-classes" }}widget-content-frameless{{ end }}
{{ define "widget-content-classes" }}{{ if not .Filters.AllFiltered }}widget-content-frameless{{ end }}{{ end }}
{{ define "widget-content" }}
{{ if .Filters.AllFiltered }}
<p>No videos match the specified filters ({{ .Filters.FilteredCount }} filtered)</p>
{{ else }}
<div class="cards-grid collapsible-container" data-collapse-after-rows="{{ .CollapseAfterRows }}">
{{ range .Videos }}
<div class="card widget-content-frame thumbnail-parent">
@ -11,3 +14,4 @@
{{ end }}
</div>
{{ end }}
{{ end }}

@ -1,6 +1,9 @@
{{ template "widget-base.html" . }}
{{- define "widget-content" }}
{{ if .Filters.AllFiltered }}
<p>No videos match the specified filters ({{ .Filters.FilteredCount }} filtered)</p>
{{ else }}
<ul class="list list-gap-14 collapsible-container" data-collapse-after="{{ .CollapseAfter }}">
{{- range .Videos }}
<li class="flex thumbnail-parent gap-10 items-center">
@ -17,4 +20,5 @@
</li>
{{- end }}
</ul>
{{ end }}
{{- end }}

@ -1,8 +1,11 @@
{{ template "widget-base.html" . }}
{{ define "widget-content-classes" }}widget-content-frameless{{ end }}
{{ define "widget-content-classes" }}{{ if not .Filters.AllFiltered }}widget-content-frameless{{ end }}{{ end }}
{{ define "widget-content" }}
{{ if .Filters.AllFiltered }}
<p>No videos match the specified filters ({{ .Filters.FilteredCount }} filtered)</p>
{{ else }}
<div class="carousel-container">
<div class="cards-horizontal carousel-items-container">
{{ range .Videos }}
@ -13,3 +16,4 @@
</div>
</div>
{{ end }}
{{ end }}

@ -44,6 +44,8 @@ type rssWidget struct {
cachedFeedsMutex sync.Mutex
cachedFeeds map[string]*cachedRSSFeed `yaml:"-"`
Filters filterableFields[rssFeedItem] `yaml:"filters"`
}
func (widget *rssWidget) initialize() error {
@ -71,13 +73,14 @@ func (widget *rssWidget) initialize() error {
}
}
widget.NoItemsMessage = "No items were returned from the feeds."
widget.cachedFeeds = make(map[string]*cachedRSSFeed)
return nil
}
func (widget *rssWidget) update(ctx context.Context) {
widget.NoItemsMessage = "No items were returned from the feeds."
items, err := widget.fetchItemsFromFeeds()
if !widget.canContinueUpdateAfterHandlingErr(err) {
@ -88,6 +91,15 @@ func (widget *rssWidget) update(ctx context.Context) {
items.sortByNewest()
}
items = widget.Filters.Apply(items)
if widget.Filters.AllFiltered {
widget.NoItemsMessage = fmt.Sprintf(
"No items match the specified filters (%d filtered)",
widget.Filters.FilteredCount,
)
}
if len(items) > widget.Limit {
items = items[:widget.Limit]
}
@ -128,6 +140,19 @@ type rssFeedItem struct {
PublishedAt time.Time
}
func (i rssFeedItem) filterableField(field string) any {
switch field {
case "title":
return i.Title
case "description":
return i.Description
case "posted":
return i.PublishedAt
default:
return nil
}
}
type rssFeedRequest struct {
URL string `yaml:"url"`
Title string `yaml:"title"`

@ -32,6 +32,8 @@ type videosWidget struct {
Limit int `yaml:"limit"`
IncludeShorts bool `yaml:"include-shorts"`
SortBy string `yaml:"sort-by"`
Filters filterableFields[video] `yaml:"filters"`
}
func (widget *videosWidget) initialize() error {
@ -71,6 +73,8 @@ func (widget *videosWidget) update(ctx context.Context) {
return
}
videos = widget.Filters.Apply(videos)
if len(videos) > widget.Limit {
videos = videos[:widget.Limit]
}
@ -131,6 +135,19 @@ type video struct {
TimeUpdated time.Time
}
func (v video) filterableField(field string) any {
switch field {
case "title":
return v.Title
case "posted":
return v.TimePosted
case "updated":
return v.TimeUpdated
default:
return nil
}
}
type videoList []video
func (v videoList) sortByPosted() videoList {
@ -154,9 +171,8 @@ func fetchYoutubeChannelUploads(channelOrPlaylistIDs []string, videoUrlTemplate
for i := range channelOrPlaylistIDs {
var feedUrl string
if strings.HasPrefix(channelOrPlaylistIDs[i], videosWidgetPlaylistPrefix) {
feedUrl = "https://www.youtube.com/feeds/videos.xml?playlist_id=" +
strings.TrimPrefix(channelOrPlaylistIDs[i], videosWidgetPlaylistPrefix)
if after, ok := strings.CutPrefix(channelOrPlaylistIDs[i], videosWidgetPlaylistPrefix); ok {
feedUrl = "https://www.youtube.com/feeds/videos.xml?playlist_id=" + after
} 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