package filter
import (
"time"
. "github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/persistence"
)
type Options = model.QueryOptions
var defaultFilters = Eq{"missing": false}
func addDefaultFilters(options Options) Options {
if options.Filters == nil {
options.Filters = defaultFilters
} else {
options.Filters = And{defaultFilters, options.Filters}
}
return options
}
func AlbumsByNewest() Options {
return addDefaultFilters(addDefaultFilters(Options{Sort: "recently_added", Order: "desc"}))
}
func AlbumsByRecent() Options {
return addDefaultFilters(Options{Sort: "playDate", Order: "desc", Filters: Gt{"play_date": time.Time{}}})
}
func AlbumsByFrequent() Options {
return addDefaultFilters(Options{Sort: "playCount", Order: "desc", Filters: Gt{"play_count": 0}})
}
func AlbumsByRandom() Options {
return addDefaultFilters(Options{Sort: "random"})
}
func AlbumsByName() Options {
return addDefaultFilters(Options{Sort: "name"})
}
func AlbumsByArtist() Options {
return addDefaultFilters(Options{Sort: "artist"})
}
func AlbumsByArtistID(artistId string) Options {
roles := []model.Role{model.RoleAlbumArtist}
if conf.Server.Subsonic.ArtistParticipations {
roles = append(roles, model.RoleArtist)
}
return addDefaultFilters(Options{
Sort: "max_year",
Filters: persistence.ParticipantIDFilter("album", artistId, roles...),
})
}
func AlbumsByContributingArtistID(artistId string) Options {
return addDefaultFilters(Options{
Sort: "max_year",
Filters: And{
persistence.ParticipantIDFilter("album", artistId, model.RoleArtist),
persistence.NotParticipantIDFilter("album", artistId, model.RoleAlbumArtist),
},
})
}
func AlbumsByYear(fromYear, toYear int) Options {
orderOption := ""
if fromYear > toYear {
fromYear, toYear = toYear, fromYear
orderOption = "desc"
}
return addDefaultFilters(Options{
Sort: "max_year",
Order: orderOption,
Filters: Or{
And{
GtOrEq{"min_year": fromYear},
LtOrEq{"min_year": toYear},
},
And{
GtOrEq{"max_year": fromYear},
LtOrEq{"max_year": toYear},
},
},
})
}
func SongsByAlbum(albumId string) Options {
return addDefaultFilters(Options{
Filters: Eq{"album_id": albumId},
Sort: "album",
})
}
func SongsByArtistID(artistId string) Options {
return addDefaultFilters(Options{
Sort: "album",
Filters: persistence.ParticipantIDFilter("media_file", artistId, model.RoleArtist, model.RoleAlbumArtist),
})
}
func SongsByGenreAndYearRange(genre string, fromYear, toYear int) Options {
options := Options{}
ff := And{}
if genre != "" {
ff = append(ff, persistence.SongGenres.ByName(genre))
}
if fromYear != 0 {
ff = append(ff, GtOrEq{"year": fromYear})
}
if toYear != 0 {
ff = append(ff, LtOrEq{"year": toYear})
}
options.Filters = ff
return addDefaultFilters(options)
}
func ApplyLibraryFilter(opts Options, musicFolderIds []int) Options {
if len(musicFolderIds) == 0 {
return opts
}
libraryFilter := Eq{"library_id": musicFolderIds}
if opts.Filters == nil {
opts.Filters = libraryFilter
} else {
opts.Filters = And{opts.Filters, libraryFilter}
}
return opts
}
func ApplyArtistLibraryFilter(opts Options, musicFolderIds []int) Options {
if len(musicFolderIds) == 0 {
return opts
}
artistLibraryFilter := Eq{"library_artist.library_id": musicFolderIds}
if opts.Filters == nil {
opts.Filters = artistLibraryFilter
} else {
opts.Filters = And{opts.Filters, artistLibraryFilter}
}
return opts
}
func ArtistsByRole(opts Options, role model.Role) Options {
if _, ok := model.AllRoles[role.String()]; !ok {
return opts
}
roleFilter := Expr("JSON_EXTRACT(library_artist.stats, '$." + role.String() + ".m') IS NOT NULL")
if opts.Filters == nil {
opts.Filters = roleFilter
} else {
opts.Filters = And{opts.Filters, roleFilter}
}
return opts
}
func SongsByGenreID(genreIds []string) Sqlizer { return persistence.SongGenres.ByID(genreIds) }
func AlbumsByGenreID(genreIds []string) Sqlizer { return persistence.AlbumGenres.ByID(genreIds) }
func AlbumsByGenre(genre string) Options {
return addDefaultFilters(Options{Sort: "name", Filters: persistence.AlbumGenres.ByName(genre)})
}
func SongsByGenre(genre string) Options {
return addDefaultFilters(Options{Sort: "name", Filters: persistence.SongGenres.ByName(genre)})
}
func ByAlbumID(albumIds []string) Sqlizer {
return Eq{"album_id": albumIds}
}
func AlbumsByYears(years []int) Sqlizer {
return Eq{"max_year": years}
}
func SongsByYears(years []int) Sqlizer {
return Eq{"year": years}
}
func ArtistsByGenreID(genreIds []string) Sqlizer {
return persistence.AlbumArtistsByGenreID(genreIds)
}
func tagIDFilter(tagName string, cond Sqlizer) Sqlizer {
return persistence.Exists(`json_tree(tags, "$.`+tagName+`")`, And{NotEq{"atom": nil}, cond})
}
func ByStudioID(ids []string) Sqlizer {
return tagIDFilter("recordlabel", Eq{"value": ids})
}
func ByRating() Options {
return addDefaultFilters(Options{Sort: "rating", Order: "desc", Filters: Gt{"rating": 0}})
}
func ByStarred() Options {
return addDefaultFilters(Options{Sort: "starred_at", Order: "desc", Filters: Eq{"starred": true}})
}
func ArtistsByStarred() Options {
return Options{Sort: "starred_at", Order: "desc", Filters: Eq{"starred": true}}
}