feat(ui): add played filter to album list (#6207)

This commit is contained in:
David Davó 2026-09-29 04:50:13 +02:00 • committed by GitHub
commit 3a31f702b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 80 additions and 1 deletions

View file

@ -131,6 +131,7 @@ var albumFilters = sync.OnceValue(func() map[string]filterFunc {
"recently_played": recentlyPlayedFilter,
"starred": annotationBoolFilter("starred"),
"has_rating": annotationBoolFilter("rating"),
"played": annotationBoolFilter("play_count"),
"missing": booleanFilter,
"genre_id": genreFilter(AlbumGenres),
"role_total_id": allRolesFilter,

View file

@ -417,6 +417,80 @@ var _ = Describe("AlbumRepository", func() {
}
})
})
Describe("played", func() {
var playedAlbum model.Album
BeforeEach(func() {
playedAlbum = model.Album{ID: "played-album", Name: "Played Album", LibraryID: 1, SongCount: 1}
Expect(albumRepo.Put(ctx, &playedAlbum)).To(Succeed())
Expect(albumRepo.IncPlayCount(ctx, playedAlbum.ID, time.Now())).To(Succeed())
})
AfterEach(func() {
_, _ = albumRepo.executeSQL(ctx, squirrel.Delete("annotation").Where(squirrel.Eq{"item_id": playedAlbum.ID}))
_, _ = albumRepo.executeSQL(ctx, squirrel.Delete("album").Where(squirrel.Eq{"id": playedAlbum.ID}))
})
It("false includes items without annotations", func() {
res, err := albumRepo.ReadAll(ctx, rest.QueryOptions{
Filters: map[string]any{"played": "false"},
})
Expect(err).ToNot(HaveOccurred())
albums := res
var found bool
for _, a := range albums {
if a.ID == albumWithoutAnnotation.ID {
found = true
break
}
}
Expect(found).To(BeTrue(), "Album without annotation should be included in played=false filter")
})
It("true excludes items without annotations", func() {
res, err := albumRepo.ReadAll(ctx, rest.QueryOptions{
Filters: map[string]any{"played": "true"},
})
Expect(err).ToNot(HaveOccurred())
albums := res
for _, a := range albums {
Expect(a.ID).ToNot(Equal(albumWithoutAnnotation.ID))
}
})
It("true includes items with play count", func() {
res, err := albumRepo.ReadAll(ctx, rest.QueryOptions{
Filters: map[string]any{"played": "true"},
})
Expect(err).ToNot(HaveOccurred())
albums := res
var found bool
for _, a := range albums {
if a.ID == playedAlbum.ID {
found = true
Expect(a.PlayCount).To(BeNumerically(">", 0))
break
}
}
Expect(found).To(BeTrue(), "Album with play count should be included in played=true filter")
})
It("false excludes items with play count", func() {
res, err := albumRepo.ReadAll(ctx, rest.QueryOptions{
Filters: map[string]any{"played": "false"},
})
Expect(err).ToNot(HaveOccurred())
albums := res
for _, a := range albums {
Expect(a.ID).ToNot(Equal(playedAlbum.ID))
}
})
})
})
Describe("Album.PlayCount", func() {

View file

@ -91,6 +91,8 @@ var _ = Describe("Annotation Filters", func() {
Entry("starred=false", "starred", "false", "COALESCE(starred, 0) = 0", []any(nil)),
Entry("starred=True (case insensitive)", "starred", "True", "COALESCE(starred, 0) > 0", []any(nil)),
Entry("rating=true", "rating", "true", "COALESCE(rating, 0) > 0", []any(nil)),
Entry("play_count=true", "play_count", "true", "COALESCE(play_count, 0) > 0", []any(nil)),
Entry("play_count=false", "play_count", "false", "COALESCE(play_count, 0) = 0", []any(nil)),
)
It("returns nil if value is not a string", func() {

View file

@ -163,6 +163,7 @@ const AlbumFilter = (props) => {
/>
</ReferenceInput>
<NullableBooleanInput source="compilation" />
<NullableBooleanInput source="played" defaultValue={false} />
<NumberInput source="year" />
{config.enableFavourites && (
<NullableBooleanInput

View file

@ -83,7 +83,8 @@
"grouping": "Grouping",
"media": "Media",
"mood": "Mood",
"missing": "Missing"
"missing": "Missing",
"played": "Played"
},
"actions": {
"playAll": "Play",