2020-01-14 14:23:29 -09:00
|
|
|
package persistence
|
2020-01-12 14:55:55 -09:00
|
|
|
|
|
|
|
|
import (
|
2020-01-28 04:22:17 -09:00
|
|
|
"context"
|
2020-12-13 10:05:48 -09:00
|
|
|
"fmt"
|
2020-04-17 15:56:52 -08:00
|
|
|
"strings"
|
2020-01-12 14:55:55 -09:00
|
|
|
|
2020-01-28 04:22:17 -09:00
|
|
|
. "github.com/Masterminds/squirrel"
|
2022-07-30 08:43:48 -08:00
|
|
|
"github.com/beego/beego/v2/client/orm"
|
2020-01-28 04:22:17 -09:00
|
|
|
"github.com/deluan/rest"
|
2020-06-13 09:55:58 -08:00
|
|
|
"github.com/navidrome/navidrome/conf"
|
2020-01-31 05:53:19 -09:00
|
|
|
"github.com/navidrome/navidrome/log"
|
2020-01-23 15:44:08 -09:00
|
|
|
"github.com/navidrome/navidrome/model"
|
2020-01-12 14:55:55 -09:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type albumRepository struct {
|
2020-01-28 04:22:17 -09:00
|
|
|
sqlRepository
|
2020-03-21 16:00:46 -08:00
|
|
|
sqlRestful
|
2020-01-12 14:55:55 -09:00
|
|
|
}
|
|
|
|
|
|
2022-07-30 08:43:48 -08:00
|
|
|
func NewAlbumRepository(ctx context.Context, o orm.QueryExecutor) model.AlbumRepository {
|
2020-01-12 14:55:55 -09:00
|
|
|
r := &albumRepository{}
|
2020-01-28 04:22:17 -09:00
|
|
|
r.ctx = ctx
|
2020-01-19 11:37:41 -09:00
|
|
|
r.ormer = o
|
2020-01-31 05:53:19 -09:00
|
|
|
r.tableName = "album"
|
2020-03-19 11:02:11 -08:00
|
|
|
r.sortMappings = map[string]string{
|
2021-03-12 13:49:47 -09:00
|
|
|
"name": "order_album_name asc, order_album_artist_name asc",
|
|
|
|
|
"artist": "compilation asc, order_album_artist_name asc, order_album_name asc",
|
|
|
|
|
"random": "RANDOM()",
|
|
|
|
|
"max_year": "max_year asc, name, order_album_name asc",
|
|
|
|
|
"recently_added": recentlyAddedSort(),
|
2020-03-19 11:02:11 -08:00
|
|
|
}
|
2020-03-19 17:09:57 -08:00
|
|
|
r.filterMappings = map[string]filterFunc{
|
2021-07-16 14:18:22 -08:00
|
|
|
"id": idFilter(r.tableName),
|
2020-07-28 04:49:28 -08:00
|
|
|
"name": fullTextFilter,
|
|
|
|
|
"compilation": booleanFilter,
|
|
|
|
|
"artist_id": artistFilter,
|
|
|
|
|
"year": yearFilter,
|
|
|
|
|
"recently_played": recentlyPlayedFilter,
|
2020-08-14 09:35:28 -08:00
|
|
|
"starred": booleanFilter,
|
2021-04-07 07:04:36 -08:00
|
|
|
"has_rating": hasRatingFilter,
|
2020-03-19 17:09:57 -08:00
|
|
|
}
|
2020-03-19 11:02:11 -08:00
|
|
|
|
2020-01-12 14:55:55 -09:00
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 13:49:47 -09:00
|
|
|
func recentlyAddedSort() string {
|
|
|
|
|
if conf.Server.RecentlyAddedByModTime {
|
|
|
|
|
return "updated_at"
|
|
|
|
|
}
|
|
|
|
|
return "created_at"
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-28 04:49:28 -08:00
|
|
|
func recentlyPlayedFilter(field string, value interface{}) Sqlizer {
|
|
|
|
|
return Gt{"play_count": 0}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-07 07:04:36 -08:00
|
|
|
func hasRatingFilter(field string, value interface{}) Sqlizer {
|
|
|
|
|
return Gt{"rating": 0}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-27 17:06:02 -08:00
|
|
|
func yearFilter(field string, value interface{}) Sqlizer {
|
|
|
|
|
return Or{
|
|
|
|
|
And{
|
|
|
|
|
Gt{"min_year": 0},
|
|
|
|
|
LtOrEq{"min_year": value},
|
|
|
|
|
GtOrEq{"max_year": value},
|
|
|
|
|
},
|
|
|
|
|
Eq{"max_year": value},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-25 14:51:13 -08:00
|
|
|
func artistFilter(field string, value interface{}) Sqlizer {
|
2020-12-13 10:05:48 -09:00
|
|
|
return Like{"all_artist_ids": fmt.Sprintf("%%%s%%", value)}
|
2020-03-25 14:51:13 -08:00
|
|
|
}
|
|
|
|
|
|
2020-01-28 04:22:17 -09:00
|
|
|
func (r *albumRepository) CountAll(options ...model.QueryOptions) (int64, error) {
|
2021-07-16 16:20:33 -08:00
|
|
|
sql := r.newSelectWithAnnotation("album.id")
|
|
|
|
|
sql = r.withGenres(sql)
|
2021-07-16 15:41:49 -08:00
|
|
|
return r.count(sql, options...)
|
2020-01-28 04:22:17 -09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) Exists(id string) (bool, error) {
|
2021-07-16 16:20:33 -08:00
|
|
|
return r.exists(Select().Where(Eq{"album.id": id}))
|
2020-01-28 04:22:17 -09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) selectAlbum(options ...model.QueryOptions) SelectBuilder {
|
2021-07-16 16:20:33 -08:00
|
|
|
sql := r.newSelectWithAnnotation("album.id", options...).Columns("album.*")
|
2021-11-02 17:18:53 -08:00
|
|
|
if len(options) > 0 && options[0].Filters != nil {
|
|
|
|
|
s, _, _ := options[0].Filters.ToSql()
|
|
|
|
|
// If there's any reference of genre in the filter, joins with genre
|
|
|
|
|
if strings.Contains(s, "genre") {
|
|
|
|
|
sql = r.withGenres(sql)
|
|
|
|
|
// If there's no filter on genre_id, group the results by media_file.id
|
|
|
|
|
if !strings.Contains(s, "genre_id") {
|
|
|
|
|
sql = sql.GroupBy("album.id")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sql
|
2020-01-12 14:55:55 -09:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 18:22:34 -09:00
|
|
|
func (r *albumRepository) Get(id string) (*model.Album, error) {
|
2021-07-16 16:20:33 -08:00
|
|
|
sq := r.selectAlbum().Where(Eq{"album.id": id})
|
2020-05-22 11:23:42 -08:00
|
|
|
var res model.Albums
|
|
|
|
|
if err := r.queryAll(sq, &res); err != nil {
|
2020-01-12 14:55:55 -09:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-05-22 11:23:42 -08:00
|
|
|
if len(res) == 0 {
|
|
|
|
|
return nil, model.ErrNotFound
|
|
|
|
|
}
|
2021-07-16 13:15:34 -08:00
|
|
|
err := r.loadAlbumGenres(&res)
|
|
|
|
|
return &res[0], err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) Put(m *model.Album) error {
|
|
|
|
|
_, err := r.put(m.ID, m)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2021-07-31 21:21:20 -08:00
|
|
|
return r.updateGenres(m.ID, r.tableName, m.Genres)
|
2020-01-12 14:55:55 -09:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 18:22:34 -09:00
|
|
|
func (r *albumRepository) GetAll(options ...model.QueryOptions) (model.Albums, error) {
|
2021-11-02 17:18:53 -08:00
|
|
|
res, err := r.GetAllWithoutGenres(options...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
err = r.loadAlbumGenres(&res)
|
|
|
|
|
return res, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) GetAllWithoutGenres(options ...model.QueryOptions) (model.Albums, error) {
|
2021-07-16 16:20:33 -08:00
|
|
|
sq := r.selectAlbum(options...)
|
2020-01-31 12:47:13 -09:00
|
|
|
res := model.Albums{}
|
2020-01-28 04:22:17 -09:00
|
|
|
err := r.queryAll(sq, &res)
|
2021-07-16 13:15:34 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-01-28 04:22:17 -09:00
|
|
|
return res, err
|
2020-01-12 14:55:55 -09:00
|
|
|
}
|
|
|
|
|
|
2020-05-18 16:32:01 -08:00
|
|
|
func (r *albumRepository) purgeEmpty() error {
|
2020-01-31 13:57:06 -09:00
|
|
|
del := Delete(r.tableName).Where("id not in (select distinct(album_id) from media_file)")
|
|
|
|
|
c, err := r.executeSQL(del)
|
2020-01-31 05:53:19 -09:00
|
|
|
if err == nil {
|
|
|
|
|
if c > 0 {
|
2020-01-31 13:57:06 -09:00
|
|
|
log.Debug(r.ctx, "Purged empty albums", "totalDeleted", c)
|
2020-01-31 05:53:19 -09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return err
|
2020-01-17 19:28:11 -09:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 18:22:34 -09:00
|
|
|
func (r *albumRepository) Search(q string, offset int, size int) (model.Albums, error) {
|
2020-01-31 12:47:13 -09:00
|
|
|
results := model.Albums{}
|
2020-01-31 11:42:48 -09:00
|
|
|
err := r.doSearch(q, offset, size, &results, "name")
|
|
|
|
|
return results, err
|
2020-01-28 04:22:17 -09:00
|
|
|
}
|
2020-01-13 11:41:14 -09:00
|
|
|
|
2020-01-28 04:22:17 -09:00
|
|
|
func (r *albumRepository) Count(options ...rest.QueryOptions) (int64, error) {
|
|
|
|
|
return r.CountAll(r.parseRestOptions(options...))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) Read(id string) (interface{}, error) {
|
|
|
|
|
return r.Get(id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) ReadAll(options ...rest.QueryOptions) (interface{}, error) {
|
|
|
|
|
return r.GetAll(r.parseRestOptions(options...))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) EntityName() string {
|
|
|
|
|
return "album"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *albumRepository) NewInstance() interface{} {
|
|
|
|
|
return &model.Album{}
|
2020-01-12 14:55:55 -09:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 18:22:34 -09:00
|
|
|
var _ model.AlbumRepository = (*albumRepository)(nil)
|
2020-01-28 04:22:17 -09:00
|
|
|
var _ model.ResourceRepository = (*albumRepository)(nil)
|