diff --git a/internal/glance/templates/videos-grid.html b/internal/glance/templates/videos-grid.html
index 2819fe8..4c87384 100644
--- a/internal/glance/templates/videos-grid.html
+++ b/internal/glance/templates/videos-grid.html
@@ -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 }}
+
{{ range .Videos }}
@@ -11,3 +14,4 @@
{{ end }}
{{ end }}
+{{ end }}
diff --git a/internal/glance/templates/videos-vertical-list.html b/internal/glance/templates/videos-vertical-list.html
index cd760a4..a6639f2 100644
--- a/internal/glance/templates/videos-vertical-list.html
+++ b/internal/glance/templates/videos-vertical-list.html
@@ -1,6 +1,9 @@
{{ template "widget-base.html" . }}
{{- define "widget-content" }}
+{{ if .Filters.AllFiltered }}
+
No videos match the specified filters ({{ .Filters.FilteredCount }} filtered)
+{{ else }}
{{- range .Videos }}
-
@@ -17,4 +20,5 @@
{{- end }}
+{{ end }}
{{- end }}
diff --git a/internal/glance/templates/videos.html b/internal/glance/templates/videos.html
index 16e7261..8a09b18 100644
--- a/internal/glance/templates/videos.html
+++ b/internal/glance/templates/videos.html
@@ -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 }}
+
No videos match the specified filters ({{ .Filters.FilteredCount }} filtered)
+{{ else }}
{{ range .Videos }}
@@ -13,3 +16,4 @@
{{ end }}
+{{ end }}
diff --git a/internal/glance/widget-rss.go b/internal/glance/widget-rss.go
index fe17b2f..cf036bd 100644
--- a/internal/glance/widget-rss.go
+++ b/internal/glance/widget-rss.go
@@ -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"`
diff --git a/internal/glance/widget-videos.go b/internal/glance/widget-videos.go
index ecf77fa..569f1da 100644
--- a/internal/glance/widget-videos.go
+++ b/internal/glance/widget-videos.go
@@ -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