From 4c0250f9f8c4463a20a494a1d16f46b22acc517d Mon Sep 17 00:00:00 2001
From: Deluan <deluan@deluan.com>
Date: Fri, 17 Apr 2020 21:18:04 -0400
Subject: [PATCH] Add fromYear/toYear params to getRandomSongs

---
 engine/list_generator.go            | 91 +++++++++++++++++------------
 server/subsonic/album_lists.go      |  6 +-
 server/subsonic/album_lists_test.go |  2 +-
 3 files changed, 59 insertions(+), 40 deletions(-)

diff --git a/engine/list_generator.go b/engine/list_generator.go
index 0ffe6f3ce..5ca1d703e 100644
--- a/engine/list_generator.go
+++ b/engine/list_generator.go
@@ -11,58 +11,58 @@ import (
 type ListGenerator interface {
 	GetAllStarred(ctx context.Context) (artists Entries, albums Entries, mediaFiles Entries, err error)
 	GetNowPlaying(ctx context.Context) (Entries, error)
-	GetRandomSongs(ctx context.Context, size int, genre string) (Entries, error)
 	GetSongsByGenre(ctx context.Context, offset int, count int, genre string) (Entries, error)
-	GetAlbums(ctx context.Context, offset, size int, filter AlbumFilter) (Entries, error)
+	GetSongs(ctx context.Context, offset, size int, filter ListFilter) (Entries, error)
+	GetAlbums(ctx context.Context, offset, size int, filter ListFilter) (Entries, error)
 }
 
 func NewListGenerator(ds model.DataStore, npRepo NowPlayingRepository) ListGenerator {
 	return &listGenerator{ds, npRepo}
 }
 
-type AlbumFilter model.QueryOptions
+type ListFilter model.QueryOptions
 
-func ByNewest() AlbumFilter {
-	return AlbumFilter{Sort: "createdAt", Order: "desc"}
+func ByNewest() ListFilter {
+	return ListFilter{Sort: "createdAt", Order: "desc"}
 }
 
-func ByRecent() AlbumFilter {
-	return AlbumFilter{Sort: "playDate", Order: "desc", Filters: squirrel.Gt{"play_date": time.Time{}}}
+func ByRecent() ListFilter {
+	return ListFilter{Sort: "playDate", Order: "desc", Filters: squirrel.Gt{"play_date": time.Time{}}}
 }
 
-func ByFrequent() AlbumFilter {
-	return AlbumFilter{Sort: "playCount", Order: "desc", Filters: squirrel.Gt{"play_count": 0}}
+func ByFrequent() ListFilter {
+	return ListFilter{Sort: "playCount", Order: "desc", Filters: squirrel.Gt{"play_count": 0}}
 }
 
-func ByRandom() AlbumFilter {
-	return AlbumFilter{Sort: "random()"}
+func ByRandom() ListFilter {
+	return ListFilter{Sort: "random()"}
 }
 
-func ByName() AlbumFilter {
-	return AlbumFilter{Sort: "name"}
+func ByName() ListFilter {
+	return ListFilter{Sort: "name"}
 }
 
-func ByArtist() AlbumFilter {
-	return AlbumFilter{Sort: "artist"}
+func ByArtist() ListFilter {
+	return ListFilter{Sort: "artist"}
 }
 
-func ByStarred() AlbumFilter {
-	return AlbumFilter{Sort: "starred_at", Order: "desc", Filters: squirrel.Eq{"starred": true}}
+func ByStarred() ListFilter {
+	return ListFilter{Sort: "starred_at", Order: "desc", Filters: squirrel.Eq{"starred": true}}
 }
 
-func ByRating() AlbumFilter {
-	return AlbumFilter{Sort: "Rating", Order: "desc", Filters: squirrel.Gt{"rating": 0}}
+func ByRating() ListFilter {
+	return ListFilter{Sort: "Rating", Order: "desc", Filters: squirrel.Gt{"rating": 0}}
 }
 
-func ByGenre(genre string) AlbumFilter {
-	return AlbumFilter{
+func ByGenre(genre string) ListFilter {
+	return ListFilter{
 		Sort:    "genre asc, name asc",
 		Filters: squirrel.Eq{"genre": genre},
 	}
 }
 
-func ByYear(fromYear, toYear int) AlbumFilter {
-	return AlbumFilter{
+func ByYear(fromYear, toYear int) ListFilter {
+	return ListFilter{
 		Sort: "max_year, name",
 		Filters: squirrel.Or{
 			squirrel.And{
@@ -77,6 +77,24 @@ func ByYear(fromYear, toYear int) AlbumFilter {
 	}
 }
 
+func RandomSongs(genre string, fromYear, toYear int) ListFilter {
+	options := ListFilter{
+		Sort: "random()",
+	}
+	ff := squirrel.And{}
+	if genre != "" {
+		options.Filters = append(ff, squirrel.Eq{"genre": genre})
+	}
+	if fromYear != 0 {
+		options.Filters = append(ff, squirrel.GtOrEq{"year": fromYear})
+	}
+	if toYear != 0 {
+		options.Filters = append(ff, squirrel.LtOrEq{"year": toYear})
+	}
+	options.Filters = ff
+	return options
+}
+
 type listGenerator struct {
 	ds     model.DataStore
 	npRepo NowPlayingRepository
@@ -94,19 +112,6 @@ func (g *listGenerator) query(ctx context.Context, qo model.QueryOptions) (Entri
 	return FromAlbums(albums), err
 }
 
-func (g *listGenerator) GetRandomSongs(ctx context.Context, size int, genre string) (Entries, error) {
-	options := model.QueryOptions{Max: size}
-	if genre != "" {
-		options.Filters = squirrel.Eq{"genre": genre}
-	}
-	mediaFiles, err := g.ds.MediaFile(ctx).GetRandom(options)
-	if err != nil {
-		return nil, err
-	}
-
-	return FromMediaFiles(mediaFiles), nil
-}
-
 func (g *listGenerator) GetSongsByGenre(ctx context.Context, offset int, count int, genre string) (Entries, error) {
 	options := model.QueryOptions{Offset: offset, Max: count}
 	if genre != "" {
@@ -120,7 +125,19 @@ func (g *listGenerator) GetSongsByGenre(ctx context.Context, offset int, count i
 	return FromMediaFiles(mediaFiles), nil
 }
 
-func (g *listGenerator) GetAlbums(ctx context.Context, offset, size int, filter AlbumFilter) (Entries, error) {
+func (g *listGenerator) GetSongs(ctx context.Context, offset, size int, filter ListFilter) (Entries, error) {
+	qo := model.QueryOptions(filter)
+	qo.Offset = offset
+	qo.Max = size
+	mediaFiles, err := g.ds.MediaFile(ctx).GetAll(qo)
+	if err != nil {
+		return nil, err
+	}
+
+	return FromMediaFiles(mediaFiles), nil
+}
+
+func (g *listGenerator) GetAlbums(ctx context.Context, offset, size int, filter ListFilter) (Entries, error) {
 	qo := model.QueryOptions(filter)
 	qo.Offset = offset
 	qo.Max = size
diff --git a/server/subsonic/album_lists.go b/server/subsonic/album_lists.go
index 780e61fde..df9482e53 100644
--- a/server/subsonic/album_lists.go
+++ b/server/subsonic/album_lists.go
@@ -27,7 +27,7 @@ func (c *AlbumListController) getNewAlbumList(r *http.Request) (engine.Entries,
 		return nil, err
 	}
 
-	var filter engine.AlbumFilter
+	var filter engine.ListFilter
 	switch typ {
 	case "newest":
 		filter = engine.ByNewest()
@@ -141,8 +141,10 @@ func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Reque
 func (c *AlbumListController) GetRandomSongs(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
 	size := utils.MinInt(utils.ParamInt(r, "size", 10), 500)
 	genre := utils.ParamString(r, "genre")
+	fromYear := utils.ParamInt(r, "fromYear", 0)
+	toYear := utils.ParamInt(r, "toYear", 0)
 
-	songs, err := c.listGen.GetRandomSongs(r.Context(), size, genre)
+	songs, err := c.listGen.GetSongs(r.Context(), 0, size, engine.RandomSongs(genre, fromYear, toYear))
 	if err != nil {
 		log.Error(r, "Error retrieving random songs", "error", err)
 		return nil, NewError(responses.ErrorGeneric, "Internal Error")
diff --git a/server/subsonic/album_lists_test.go b/server/subsonic/album_lists_test.go
index f29a267ef..788a55287 100644
--- a/server/subsonic/album_lists_test.go
+++ b/server/subsonic/album_lists_test.go
@@ -18,7 +18,7 @@ type fakeListGen struct {
 	recvSize   int
 }
 
-func (lg *fakeListGen) GetAlbums(ctx context.Context, offset int, size int, filter engine.AlbumFilter) (engine.Entries, error) {
+func (lg *fakeListGen) GetAlbums(ctx context.Context, offset int, size int, filter engine.ListFilter) (engine.Entries, error) {
 	if lg.err != nil {
 		return nil, lg.err
 	}